From 1567c3e3467cddeb019a7b53ec632f834b6a9239 Mon Sep 17 00:00:00 2001 From: Giovanni Gherdovich Date: Wed, 22 Jan 2020 16:16:12 +0100 Subject: [PATCH 0001/2231] x86, sched: Add support for frequency invariance Implement arch_scale_freq_capacity() for 'modern' x86. This function is used by the scheduler to correctly account usage in the face of DVFS. The present patch addresses Intel processors specifically and has positive performance and performance-per-watt implications for the schedutil cpufreq governor, bringing it closer to, if not on-par with, the powersave governor from the intel_pstate driver/framework. Large performance gains are obtained when the machine is lightly loaded and no regression are observed at saturation. The benchmarks with the largest gains are kernel compilation, tbench (the networking version of dbench) and shell-intensive workloads. 1. FREQUENCY INVARIANCE: MOTIVATION * Without it, a task looks larger if the CPU runs slower 2. PECULIARITIES OF X86 * freq invariance accounting requires knowing the ratio freq_curr/freq_max 2.1 CURRENT FREQUENCY * Use delta_APERF / delta_MPERF * freq_base (a.k.a "BusyMHz") 2.2 MAX FREQUENCY * It varies with time (turbo). As an approximation, we set it to a constant, i.e. 4-cores turbo frequency. 3. EFFECTS ON THE SCHEDUTIL FREQUENCY GOVERNOR * The invariant schedutil's formula has no feedback loop and reacts faster to utilization changes 4. KNOWN LIMITATIONS * In some cases tasks can't reach max util despite how hard they try 5. PERFORMANCE TESTING 5.1 MACHINES * Skylake, Broadwell, Haswell 5.2 SETUP * baseline Linux v5.2 w/ non-invariant schedutil. Tested freq_max = 1-2-3-4-8-12 active cores turbo w/ invariant schedutil, and intel_pstate/powersave 5.3 BENCHMARK RESULTS 5.3.1 NEUTRAL BENCHMARKS * NAS Parallel Benchmark (HPC), hackbench 5.3.2 NON-NEUTRAL BENCHMARKS * tbench (10-30% better), kernbench (10-15% better), shell-intensive-scripts (30-50% better) * no regressions 5.3.3 SELECTION OF DETAILED RESULTS 5.3.4 POWER CONSUMPTION, PERFORMANCE-PER-WATT * dbench (5% worse on one machine), kernbench (3% worse), tbench (5-10% better), shell-intensive-scripts (10-40% better) 6. MICROARCH'ES ADDRESSED HERE * Xeon Core before Scalable Performance processors line (Xeon Gold/Platinum etc have different MSRs semantic for querying turbo levels) 7. REFERENCES * MMTests performance testing framework, github.com/gormanm/mmtests +-------------------------------------------------------------------------+ | 1. FREQUENCY INVARIANCE: MOTIVATION +-------------------------------------------------------------------------+ For example; suppose a CPU has two frequencies: 500 and 1000 Mhz. When running a task that would consume 1/3rd of a CPU at 1000 MHz, it would appear to consume 2/3rd (or 66.6%) when running at 500 MHz, giving the false impression this CPU is almost at capacity, even though it can go faster [*]. In a nutshell, without frequency scale-invariance tasks look larger just because the CPU is running slower. [*] (footnote: this assumes a linear frequency/performance relation; which everybody knows to be false, but given realities its the best approximation we can make.) +-------------------------------------------------------------------------+ | 2. PECULIARITIES OF X86 +-------------------------------------------------------------------------+ Accounting for frequency changes in PELT signals requires the computation of the ratio freq_curr / freq_max. On x86 neither of those terms is readily available. 2.1 CURRENT FREQUENCY ==================== Since modern x86 has hardware control over the actual frequency we run at (because amongst other things, Turbo-Mode), we cannot simply use the frequency as requested through cpufreq. Instead we use the APERF/MPERF MSRs to compute the effective frequency over the recent past. Also, because reading MSRs is expensive, don't do so every time we need the value, but amortize the cost by doing it every tick. 2.2 MAX FREQUENCY ================= Obtaining freq_max is also non-trivial because at any time the hardware can provide a frequency boost to a selected subset of cores if the package has enough power to spare (eg: Turbo Boost). This means that the maximum frequency available to a given core changes with time. The approach taken in this change is to arbitrarily set freq_max to a constant value at boot. The value chosen is the "4-cores (4C) turbo frequency" on most microarchitectures, after evaluating the following candidates: * 1-core (1C) turbo frequency (the fastest turbo state available) * around base frequency (a.k.a. max P-state) * something in between, such as 4C turbo To interpret these options, consider that this is the denominator in freq_curr/freq_max, and that ratio will be used to scale PELT signals such as util_avg and load_avg. A large denominator will undershoot (util_avg looks a bit smaller than it really is), viceversa with a smaller denominator PELT signals will tend to overshoot. Given that PELT drives frequency selection in the schedutil governor, we will have: freq_max set to | effect on DVFS --------------------+------------------ 1C turbo | power efficiency (lower freq choices) base freq | performance (higher util_avg, higher freq requests) 4C turbo | a bit of both 4C turbo proves to be a good compromise in a number of benchmarks (see below). +-------------------------------------------------------------------------+ | 3. EFFECTS ON THE SCHEDUTIL FREQUENCY GOVERNOR +-------------------------------------------------------------------------+ Once an architecture implements a frequency scale-invariant utilization (the PELT signal util_avg), schedutil switches its frequency selection formula from freq_next = 1.25 * freq_curr * util [non-invariant util signal] to freq_next = 1.25 * freq_max * util [invariant util signal] where, in the second formula, freq_max is set to the 1C turbo frequency (max turbo). The advantage of the second formula, whose usage we unlock with this patch, is that freq_next doesn't depend on the current frequency in an iterative fashion, but can jump to any frequency in a single update. This absence of feedback in the formula makes it quicker to react to utilization changes and more robust against pathological instabilities. Compare it to the update formula of intel_pstate/powersave: freq_next = 1.25 * freq_max * Busy% where again freq_max is 1C turbo and Busy% is the percentage of time not spent idling (calculated with delta_MPERF / delta_TSC); essentially the same as invariant schedutil, and largely responsible for intel_pstate/powersave good reputation. The non-invariant schedutil formula is derived from the invariant one by approximating util_inv with util_raw * freq_curr / freq_max, but this has limitations. Testing shows improved performances due to better frequency selections when the machine is lightly loaded, and essentially no change in behaviour at saturation / overutilization. +-------------------------------------------------------------------------+ | 4. KNOWN LIMITATIONS +-------------------------------------------------------------------------+ It's been shown that it is possible to create pathological scenarios where a CPU-bound task cannot reach max utilization, if the normalizing factor freq_max is fixed to a constant value (see [Lelli-2018]). If freq_max is set to 4C turbo as we do here, one needs to peg at least 5 cores in a package doing some busywork, and observe that none of those task will ever reach max util (1024) because they're all running at less than the 4C turbo frequency. While this concern still applies, we believe the performance benefit of frequency scale-invariant PELT signals outweights the cost of this limitation. [Lelli-2018] https://lore.kernel.org/lkml/20180517150418.GF22493@localhost.localdomain/ +-------------------------------------------------------------------------+ | 5. PERFORMANCE TESTING +-------------------------------------------------------------------------+ 5.1 MACHINES ============ We tested the patch on three machines, with Skylake, Broadwell and Haswell CPUs. The details are below, together with the available turbo ratios as reported by the appropriate MSRs. * 8x-SKYLAKE-UMA: Single socket E3-1240 v5, Skylake 4 cores/8 threads Max EFFiciency, BASE frequency and available turbo levels (MHz): EFFIC 800 |******** BASE 3500 |*********************************** 4C 3700 |************************************* 3C 3800 |************************************** 2C 3900 |*************************************** 1C 3900 |*************************************** * 80x-BROADWELL-NUMA: Two sockets E5-2698 v4, 2x Broadwell 20 cores/40 threads Max EFFiciency, BASE frequency and available turbo levels (MHz): EFFIC 1200 |************ BASE 2200 |********************** 8C 2900 |***************************** 7C 3000 |****************************** 6C 3100 |******************************* 5C 3200 |******************************** 4C 3300 |********************************* 3C 3400 |********************************** 2C 3600 |************************************ 1C 3600 |************************************ * 48x-HASWELL-NUMA Two sockets E5-2670 v3, 2x Haswell 12 cores/24 threads Max EFFiciency, BASE frequency and available turbo levels (MHz): EFFIC 1200 |************ BASE 2300 |*********************** 12C 2600 |************************** 11C 2600 |************************** 10C 2600 |************************** 9C 2600 |************************** 8C 2600 |************************** 7C 2600 |************************** 6C 2600 |************************** 5C 2700 |*************************** 4C 2800 |**************************** 3C 2900 |***************************** 2C 3100 |******************************* 1C 3100 |******************************* 5.2 SETUP ========= * The baseline is Linux v5.2 with schedutil (non-invariant) and the intel_pstate driver in passive mode. * The rationale for choosing the various freq_max values to test have been to try all the 1-2-3-4C turbo levels (note that 1C and 2C turbo are identical on all machines), plus one more value closer to base_freq but still in the turbo range (8C turbo for both 80x-BROADWELL-NUMA and 48x-HASWELL-NUMA). * In addition we've run all tests with intel_pstate/powersave for comparison. * The filesystem is always XFS, the userspace is openSUSE Leap 15.1. * 8x-SKYLAKE-UMA is capable of HWP (Hardware-Managed P-States), so the runs with active intel_pstate on this machine use that. This gives, in terms of combinations tested on each machine: * 8x-SKYLAKE-UMA * Baseline: Linux v5.2, non-invariant schedutil, intel_pstate passive * intel_pstate active + powersave + HWP * invariant schedutil, freq_max = 1C turbo * invariant schedutil, freq_max = 3C turbo * invariant schedutil, freq_max = 4C turbo * both 80x-BROADWELL-NUMA and 48x-HASWELL-NUMA * [same as 8x-SKYLAKE-UMA, but no HWP capable] * invariant schedutil, freq_max = 8C turbo (which on 48x-HASWELL-NUMA is the same as 12C turbo, or "all cores turbo") 5.3 BENCHMARK RESULTS ===================== 5.3.1 NEUTRAL BENCHMARKS ------------------------ Tests that didn't show any measurable difference in performance on any of the test machines between non-invariant schedutil and our patch are: * NAS Parallel Benchmarks (NPB) using either MPI or openMP for IPC, any computational kernel * flexible I/O (FIO) * hackbench (using threads or processes, and using pipes or sockets) 5.3.2 NON-NEUTRAL BENCHMARKS ---------------------------- What follow are summary tables where each benchmark result is given a score. * A tilde (~) means a neutral result, i.e. no difference from baseline. * Scores are computed with the ratio result_new / result_baseline, so a tilde means a score of 1.00. * The results in the score ratio are the geometric means of results running the benchmark with different parameters (eg: for kernbench: using 1, 2, 4, ... number of processes; for pgbench: varying the number of clients, and so on). * The first three tables show higher-is-better kind of tests (i.e. measured in operations/second), the subsequent three show lower-is-better kind of tests (i.e. the workload is fixed and we measure elapsed time, think kernbench). * "gitsource" is a name we made up for the test consisting in running the entire unit tests suite of the Git SCM and measuring how long it takes. We take it as a typical example of shell-intensive serialized workload. * In the "I_PSTATE" column we have the results for intel_pstate/powersave. Other columns show invariant schedutil for different values of freq_max. 4C turbo is circled as it's the value we've chosen for the final implementation. 80x-BROADWELL-NUMA (comparison ratio; higher is better) +------+ I_PSTATE 1C 3C | 4C | 8C pgbench-ro 1.14 ~ ~ | 1.11 | 1.14 pgbench-rw ~ ~ ~ | ~ | ~ netperf-udp 1.06 ~ 1.06 | 1.05 | 1.07 netperf-tcp ~ 1.03 ~ | 1.01 | 1.02 tbench4 1.57 1.18 1.22 | 1.30 | 1.56 +------+ 8x-SKYLAKE-UMA (comparison ratio; higher is better) +------+ I_PSTATE/HWP 1C 3C | 4C | pgbench-ro ~ ~ ~ | ~ | pgbench-rw ~ ~ ~ | ~ | netperf-udp ~ ~ ~ | ~ | netperf-tcp ~ ~ ~ | ~ | tbench4 1.30 1.14 1.14 | 1.16 | +------+ 48x-HASWELL-NUMA (comparison ratio; higher is better) +------+ I_PSTATE 1C 3C | 4C | 12C pgbench-ro 1.15 ~ ~ | 1.06 | 1.16 pgbench-rw ~ ~ ~ | ~ | ~ netperf-udp 1.05 0.97 1.04 | 1.04 | 1.02 netperf-tcp 0.96 1.01 1.01 | 1.01 | 1.01 tbench4 1.50 1.05 1.13 | 1.13 | 1.25 +------+ In the table above we see that active intel_pstate is slightly better than our 4C-turbo patch (both in reference to the baseline non-invariant schedutil) on read-only pgbench and much better on tbench. Both cases are notable in which it shows that lowering our freq_max (to 8C-turbo and 12C-turbo on 80x-BROADWELL-NUMA and 48x-HASWELL-NUMA respectively) helps invariant schedutil to get closer. If we ignore active intel_pstate and focus on the comparison with baseline alone, there are several instances of double-digit performance improvement. 80x-BROADWELL-NUMA (comparison ratio; lower is better) +------+ I_PSTATE 1C 3C | 4C | 8C dbench4 1.23 0.95 0.95 | 0.95 | 0.95 kernbench 0.93 0.83 0.83 | 0.83 | 0.82 gitsource 0.98 0.49 0.49 | 0.49 | 0.48 +------+ 8x-SKYLAKE-UMA (comparison ratio; lower is better) +------+ I_PSTATE/HWP 1C 3C | 4C | dbench4 ~ ~ ~ | ~ | kernbench ~ ~ ~ | ~ | gitsource 0.92 0.55 0.55 | 0.55 | +------+ 48x-HASWELL-NUMA (comparison ratio; lower is better) +------+ I_PSTATE 1C 3C | 4C | 8C dbench4 ~ ~ ~ | ~ | ~ kernbench 0.94 0.90 0.89 | 0.90 | 0.90 gitsource 0.97 0.69 0.69 | 0.69 | 0.69 +------+ dbench is not very remarkable here, unless we notice how poorly active intel_pstate is performing on 80x-BROADWELL-NUMA: 23% regression versus non-invariant schedutil. We repeated that run getting consistent results. Out of scope for the patch at hand, but deserving future investigation. Other than that, we previously ran this campaign with Linux v5.0 and saw the patch doing better on dbench a the time. We haven't checked closely and can only speculate at this point. On the NUMA boxes kernbench gets 10-15% improvements on average; we'll see in the detailed tables that the gains concentrate on low process counts (lightly loaded machines). The test we call "gitsource" (running the git unit test suite, a long-running single-threaded shell script) appears rather spectacular in this table (gains of 30-50% depending on the machine). It is to be noted, however, that gitsource has no adjustable parameters (such as the number of jobs in kernbench, which we average over in order to get a single-number summary score) and is exactly the kind of low-parallelism workload that benefits the most from this patch. When looking at the detailed tables of kernbench or tbench4, at low process or client counts one can see similar numbers. 5.3.3 SELECTION OF DETAILED RESULTS ----------------------------------- Machine : 48x-HASWELL-NUMA Benchmark : tbench4 (i.e. dbench4 over the network, actually loopback) Varying parameter : number of clients Unit : MB/sec (higher is better) 5.2.0 vanilla (BASELINE) 5.2.0 intel_pstate 5.2.0 1C-turbo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Hmean 1 126.73 +- 0.31% ( ) 315.91 +- 0.66% ( 149.28%) 125.03 +- 0.76% ( -1.34%) Hmean 2 258.04 +- 0.62% ( ) 614.16 +- 0.51% ( 138.01%) 269.58 +- 1.45% ( 4.47%) Hmean 4 514.30 +- 0.67% ( ) 1146.58 +- 0.54% ( 122.94%) 533.84 +- 1.99% ( 3.80%) Hmean 8 1111.38 +- 2.52% ( ) 2159.78 +- 0.38% ( 94.33%) 1359.92 +- 1.56% ( 22.36%) Hmean 16 2286.47 +- 1.36% ( ) 3338.29 +- 0.21% ( 46.00%) 2720.20 +- 0.52% ( 18.97%) Hmean 32 4704.84 +- 0.35% ( ) 4759.03 +- 0.43% ( 1.15%) 4774.48 +- 0.30% ( 1.48%) Hmean 64 7578.04 +- 0.27% ( ) 7533.70 +- 0.43% ( -0.59%) 7462.17 +- 0.65% ( -1.53%) Hmean 128 6998.52 +- 0.16% ( ) 6987.59 +- 0.12% ( -0.16%) 6909.17 +- 0.14% ( -1.28%) Hmean 192 6901.35 +- 0.25% ( ) 6913.16 +- 0.10% ( 0.17%) 6855.47 +- 0.21% ( -0.66%) 5.2.0 3C-turbo 5.2.0 4C-turbo 5.2.0 12C-turbo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Hmean 1 128.43 +- 0.28% ( 1.34%) 130.64 +- 3.81% ( 3.09%) 153.71 +- 5.89% ( 21.30%) Hmean 2 311.70 +- 6.15% ( 20.79%) 281.66 +- 3.40% ( 9.15%) 305.08 +- 5.70% ( 18.23%) Hmean 4 641.98 +- 2.32% ( 24.83%) 623.88 +- 5.28% ( 21.31%) 906.84 +- 4.65% ( 76.32%) Hmean 8 1633.31 +- 1.56% ( 46.96%) 1714.16 +- 0.93% ( 54.24%) 2095.74 +- 0.47% ( 88.57%) Hmean 16 3047.24 +- 0.42% ( 33.27%) 3155.02 +- 0.30% ( 37.99%) 3634.58 +- 0.15% ( 58.96%) Hmean 32 4734.31 +- 0.60% ( 0.63%) 4804.38 +- 0.23% ( 2.12%) 4674.62 +- 0.27% ( -0.64%) Hmean 64 7699.74 +- 0.35% ( 1.61%) 7499.72 +- 0.34% ( -1.03%) 7659.03 +- 0.25% ( 1.07%) Hmean 128 6935.18 +- 0.15% ( -0.91%) 6942.54 +- 0.10% ( -0.80%) 7004.85 +- 0.12% ( 0.09%) Hmean 192 6901.62 +- 0.12% ( 0.00%) 6856.93 +- 0.10% ( -0.64%) 6978.74 +- 0.10% ( 1.12%) This is one of the cases where the patch still can't surpass active intel_pstate, not even when freq_max is as low as 12C-turbo. Otherwise, gains are visible up to 16 clients and the saturated scenario is the same as baseline. The scores in the summary table from the previous sections are ratios of geometric means of the results over different clients, as seen in this table. Machine : 80x-BROADWELL-NUMA Benchmark : kernbench (kernel compilation) Varying parameter : number of jobs Unit : seconds (lower is better) 5.2.0 vanilla (BASELINE) 5.2.0 intel_pstate 5.2.0 1C-turbo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Amean 2 379.68 +- 0.06% ( ) 330.20 +- 0.43% ( 13.03%) 285.93 +- 0.07% ( 24.69%) Amean 4 200.15 +- 0.24% ( ) 175.89 +- 0.22% ( 12.12%) 153.78 +- 0.25% ( 23.17%) Amean 8 106.20 +- 0.31% ( ) 95.54 +- 0.23% ( 10.03%) 86.74 +- 0.10% ( 18.32%) Amean 16 56.96 +- 1.31% ( ) 53.25 +- 1.22% ( 6.50%) 48.34 +- 1.73% ( 15.13%) Amean 32 34.80 +- 2.46% ( ) 33.81 +- 0.77% ( 2.83%) 30.28 +- 1.59% ( 12.99%) Amean 64 26.11 +- 1.63% ( ) 25.04 +- 1.07% ( 4.10%) 22.41 +- 2.37% ( 14.16%) Amean 128 24.80 +- 1.36% ( ) 23.57 +- 1.23% ( 4.93%) 21.44 +- 1.37% ( 13.55%) Amean 160 24.85 +- 0.56% ( ) 23.85 +- 1.17% ( 4.06%) 21.25 +- 1.12% ( 14.49%) 5.2.0 3C-turbo 5.2.0 4C-turbo 5.2.0 8C-turbo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Amean 2 284.08 +- 0.13% ( 25.18%) 283.96 +- 0.51% ( 25.21%) 285.05 +- 0.21% ( 24.92%) Amean 4 153.18 +- 0.22% ( 23.47%) 154.70 +- 1.64% ( 22.71%) 153.64 +- 0.30% ( 23.24%) Amean 8 87.06 +- 0.28% ( 18.02%) 86.77 +- 0.46% ( 18.29%) 86.78 +- 0.22% ( 18.28%) Amean 16 48.03 +- 0.93% ( 15.68%) 47.75 +- 1.99% ( 16.17%) 47.52 +- 1.61% ( 16.57%) Amean 32 30.23 +- 1.20% ( 13.14%) 30.08 +- 1.67% ( 13.57%) 30.07 +- 1.67% ( 13.60%) Amean 64 22.59 +- 2.02% ( 13.50%) 22.63 +- 0.81% ( 13.32%) 22.42 +- 0.76% ( 14.12%) Amean 128 21.37 +- 0.67% ( 13.82%) 21.31 +- 1.15% ( 14.07%) 21.17 +- 1.93% ( 14.63%) Amean 160 21.68 +- 0.57% ( 12.76%) 21.18 +- 1.74% ( 14.77%) 21.22 +- 1.00% ( 14.61%) The patch outperform active intel_pstate (and baseline) by a considerable margin; the summary table from the previous section says 4C turbo and active intel_pstate are 0.83 and 0.93 against baseline respectively, so 4C turbo is 0.83/0.93=0.89 against intel_pstate (~10% better on average). There is no noticeable difference with regard to the value of freq_max. Machine : 8x-SKYLAKE-UMA Benchmark : gitsource (time to run the git unit test suite) Varying parameter : none Unit : seconds (lower is better) 5.2.0 vanilla 5.2.0 intel_pstate/hwp 5.2.0 1C-turbo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Amean 858.85 +- 1.16% ( ) 791.94 +- 0.21% ( 7.79%) 474.95 ( 44.70%) 5.2.0 3C-turbo 5.2.0 4C-turbo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Amean 475.26 +- 0.20% ( 44.66%) 474.34 +- 0.13% ( 44.77%) In this test, which is of interest as representing shell-intensive (i.e. fork-intensive) serialized workloads, invariant schedutil outperforms intel_pstate/powersave by a whopping 40% margin. 5.3.4 POWER CONSUMPTION, PERFORMANCE-PER-WATT --------------------------------------------- The following table shows average power consumption in watt for each benchmark. Data comes from turbostat (package average), which in turn is read from the RAPL interface on CPUs. We know the patch affects CPU frequencies so it's reasonable to ignore other power consumers (such as memory or I/O). Also, we don't have a power meter available in the lab so RAPL is the best we have. turbostat sampled average power every 10 seconds for the entire duration of each benchmark. We took all those values and averaged them (i.e. with don't have detail on a per-parameter granularity, only on whole benchmarks). 80x-BROADWELL-NUMA (power consumption, watts) +--------+ BASELINE I_PSTATE 1C 3C | 4C | 8C pgbench-ro 130.01 142.77 131.11 132.45 | 134.65 | 136.84 pgbench-rw 68.30 60.83 71.45 71.70 | 71.65 | 72.54 dbench4 90.25 59.06 101.43 99.89 | 101.10 | 102.94 netperf-udp 65.70 69.81 66.02 68.03 | 68.27 | 68.95 netperf-tcp 88.08 87.96 88.97 88.89 | 88.85 | 88.20 tbench4 142.32 176.73 153.02 163.91 | 165.58 | 176.07 kernbench 92.94 101.95 114.91 115.47 | 115.52 | 115.10 gitsource 40.92 41.87 75.14 75.20 | 75.40 | 75.70 +--------+ 8x-SKYLAKE-UMA (power consumption, watts) +--------+ BASELINE I_PSTATE/HWP 1C 3C | 4C | pgbench-ro 46.49 46.68 46.56 46.59 | 46.52 | pgbench-rw 29.34 31.38 30.98 31.00 | 31.00 | dbench4 27.28 27.37 27.49 27.41 | 27.38 | netperf-udp 22.33 22.41 22.36 22.35 | 22.36 | netperf-tcp 27.29 27.29 27.30 27.31 | 27.33 | tbench4 41.13 45.61 43.10 43.33 | 43.56 | kernbench 42.56 42.63 43.01 43.01 | 43.01 | gitsource 13.32 13.69 17.33 17.30 | 17.35 | +--------+ 48x-HASWELL-NUMA (power consumption, watts) +--------+ BASELINE I_PSTATE 1C 3C | 4C | 12C pgbench-ro 128.84 136.04 129.87 132.43 | 132.30 | 134.86 pgbench-rw 37.68 37.92 37.17 37.74 | 37.73 | 37.31 dbench4 28.56 28.73 28.60 28.73 | 28.70 | 28.79 netperf-udp 56.70 60.44 56.79 57.42 | 57.54 | 57.52 netperf-tcp 75.49 75.27 75.87 76.02 | 76.01 | 75.95 tbench4 115.44 139.51 119.53 123.07 | 123.97 | 130.22 kernbench 83.23 91.55 95.58 95.69 | 95.72 | 96.04 gitsource 36.79 36.99 39.99 40.34 | 40.35 | 40.23 +--------+ A lower power consumption isn't necessarily better, it depends on what is done with that energy. Here are tables with the ratio of performance-per-watt on each machine and benchmark. Higher is always better; a tilde (~) means a neutral ratio (i.e. 1.00). 80x-BROADWELL-NUMA (performance-per-watt ratios; higher is better) +------+ I_PSTATE 1C 3C | 4C | 8C pgbench-ro 1.04 1.06 0.94 | 1.07 | 1.08 pgbench-rw 1.10 0.97 0.96 | 0.96 | 0.97 dbench4 1.24 0.94 0.95 | 0.94 | 0.92 netperf-udp ~ 1.02 1.02 | ~ | 1.02 netperf-tcp ~ 1.02 ~ | ~ | 1.02 tbench4 1.26 1.10 1.06 | 1.12 | 1.26 kernbench 0.98 0.97 0.97 | 0.97 | 0.98 gitsource ~ 1.11 1.11 | 1.11 | 1.13 +------+ 8x-SKYLAKE-UMA (performance-per-watt ratios; higher is better) +------+ I_PSTATE/HWP 1C 3C | 4C | pgbench-ro ~ ~ ~ | ~ | pgbench-rw 0.95 0.97 0.96 | 0.96 | dbench4 ~ ~ ~ | ~ | netperf-udp ~ ~ ~ | ~ | netperf-tcp ~ ~ ~ | ~ | tbench4 1.17 1.09 1.08 | 1.10 | kernbench ~ ~ ~ | ~ | gitsource 1.06 1.40 1.40 | 1.40 | +------+ 48x-HASWELL-NUMA (performance-per-watt ratios; higher is better) +------+ I_PSTATE 1C 3C | 4C | 12C pgbench-ro 1.09 ~ 1.09 | 1.03 | 1.11 pgbench-rw ~ 0.86 ~ | ~ | 0.86 dbench4 ~ 1.02 1.02 | 1.02 | ~ netperf-udp ~ 0.97 1.03 | 1.02 | ~ netperf-tcp 0.96 ~ ~ | ~ | ~ tbench4 1.24 ~ 1.06 | 1.05 | 1.11 kernbench 0.97 0.97 0.98 | 0.97 | 0.96 gitsource 1.03 1.33 1.32 | 1.32 | 1.33 +------+ These results are overall pleasing: in plenty of cases we observe performance-per-watt improvements. The few regressions (read/write pgbench and dbench on the Broadwell machine) are of small magnitude. kernbench loses a few percentage points (it has a 10-15% performance improvement, but apparently the increase in power consumption is larger than that). tbench4 and gitsource, which benefit the most from the patch, keep a positive score in this table which is a welcome surprise; that suggests that in those particular workloads the non-invariant schedutil (and active intel_pstate, too) makes some rather suboptimal frequency selections. +-------------------------------------------------------------------------+ | 6. MICROARCH'ES ADDRESSED HERE +-------------------------------------------------------------------------+ The patch addresses Xeon Core processors that use MSR_PLATFORM_INFO and MSR_TURBO_RATIO_LIMIT to advertise their base frequency and turbo frequencies respectively. This excludes the recent Xeon Scalable Performance processors line (Xeon Gold, Platinum etc) whose MSRs have to be parsed differently. Subsequent patches will address: * Xeon Scalable Performance processors and Atom Goldmont/Goldmont Plus * Xeon Phi (Knights Landing, Knights Mill) * Atom Silvermont +-------------------------------------------------------------------------+ | 7. REFERENCES +-------------------------------------------------------------------------+ Tests have been run with the help of the MMTests performance testing framework, see github.com/gormanm/mmtests. The configuration file names for the benchmark used are: db-pgbench-timed-ro-small-xfs db-pgbench-timed-rw-small-xfs io-dbench4-async-xfs network-netperf-unbound network-tbench scheduler-unbound workload-kerndevel-xfs workload-shellscripts-xfs hpc-nas-c-class-mpi-full-xfs hpc-nas-c-class-omp-full All those benchmarks are generally available on the web: pgbench: https://www.postgresql.org/docs/10/pgbench.html netperf: https://hewlettpackard.github.io/netperf/ dbench/tbench: https://dbench.samba.org/ gitsource: git unit test suite, github.com/git/git NAS Parallel Benchmarks: https://www.nas.nasa.gov/publications/npb.html hackbench: https://people.redhat.com/mingo/cfs-scheduler/tools/hackbench.c Suggested-by: Peter Zijlstra Signed-off-by: Giovanni Gherdovich Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Doug Smythies Acked-by: Rafael J. Wysocki Link: https://lkml.kernel.org/r/20200122151617.531-2-ggherdovich@suse.cz --- arch/x86/include/asm/topology.h | 20 ++++ arch/x86/kernel/smpboot.c | 183 +++++++++++++++++++++++++++++++- kernel/sched/core.c | 1 + kernel/sched/sched.h | 7 ++ 4 files changed, 210 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index 4b14d2318251..2ebf7b7b2126 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -193,4 +193,24 @@ static inline void sched_clear_itmt_support(void) } #endif /* CONFIG_SCHED_MC_PRIO */ +#ifdef CONFIG_SMP +#include + +DECLARE_STATIC_KEY_FALSE(arch_scale_freq_key); + +#define arch_scale_freq_invariant() static_branch_likely(&arch_scale_freq_key) + +DECLARE_PER_CPU(unsigned long, arch_freq_scale); + +static inline long arch_scale_freq_capacity(int cpu) +{ + return per_cpu(arch_freq_scale, cpu); +} +#define arch_scale_freq_capacity arch_scale_freq_capacity + +extern void arch_scale_freq_tick(void); +#define arch_scale_freq_tick arch_scale_freq_tick + +#endif + #endif /* _ASM_X86_TOPOLOGY_H */ diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 69881b2d446c..28696bccf912 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -147,6 +147,8 @@ static inline void smpboot_restore_warm_reset_vector(void) *((volatile u32 *)phys_to_virt(TRAMPOLINE_PHYS_LOW)) = 0; } +static void init_freq_invariance(void); + /* * Report back to the Boot Processor during boot time or to the caller processor * during CPU online. @@ -183,6 +185,8 @@ static void smp_callin(void) */ set_cpu_sibling_map(raw_smp_processor_id()); + init_freq_invariance(); + /* * Get our bogomips. * Update loops_per_jiffy in cpu_data. Previous call to @@ -1337,7 +1341,7 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus) set_sched_topology(x86_topology); set_cpu_sibling_map(0); - + init_freq_invariance(); smp_sanity_check(); switch (apic_intr_mode) { @@ -1764,3 +1768,180 @@ void native_play_dead(void) } #endif + +/* + * APERF/MPERF frequency ratio computation. + * + * The scheduler wants to do frequency invariant accounting and needs a <1 + * ratio to account for the 'current' frequency, corresponding to + * freq_curr / freq_max. + * + * Since the frequency freq_curr on x86 is controlled by micro-controller and + * our P-state setting is little more than a request/hint, we need to observe + * the effective frequency 'BusyMHz', i.e. the average frequency over a time + * interval after discarding idle time. This is given by: + * + * BusyMHz = delta_APERF / delta_MPERF * freq_base + * + * where freq_base is the max non-turbo P-state. + * + * The freq_max term has to be set to a somewhat arbitrary value, because we + * can't know which turbo states will be available at a given point in time: + * it all depends on the thermal headroom of the entire package. We set it to + * the turbo level with 4 cores active. + * + * Benchmarks show that's a good compromise between the 1C turbo ratio + * (freq_curr/freq_max would rarely reach 1) and something close to freq_base, + * which would ignore the entire turbo range (a conspicuous part, making + * freq_curr/freq_max always maxed out). + * + * Setting freq_max to anything less than the 1C turbo ratio makes the ratio + * freq_curr / freq_max to eventually grow >1, in which case we clip it to 1. + */ + +DEFINE_STATIC_KEY_FALSE(arch_scale_freq_key); + +static DEFINE_PER_CPU(u64, arch_prev_aperf); +static DEFINE_PER_CPU(u64, arch_prev_mperf); +static u64 arch_max_freq_ratio = SCHED_CAPACITY_SCALE; + +static bool turbo_disabled(void) +{ + u64 misc_en; + int err; + + err = rdmsrl_safe(MSR_IA32_MISC_ENABLE, &misc_en); + if (err) + return false; + + return (misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE); +} + +#include +#include + +#define ICPU(model) \ + {X86_VENDOR_INTEL, 6, model, X86_FEATURE_APERFMPERF, 0} + +static const struct x86_cpu_id has_knl_turbo_ratio_limits[] = { + ICPU(INTEL_FAM6_XEON_PHI_KNL), + ICPU(INTEL_FAM6_XEON_PHI_KNM), + {} +}; + +static const struct x86_cpu_id has_skx_turbo_ratio_limits[] = { + ICPU(INTEL_FAM6_SKYLAKE_X), + {} +}; + +static const struct x86_cpu_id has_glm_turbo_ratio_limits[] = { + ICPU(INTEL_FAM6_ATOM_GOLDMONT), + ICPU(INTEL_FAM6_ATOM_GOLDMONT_D), + ICPU(INTEL_FAM6_ATOM_GOLDMONT_PLUS), + {} +}; + +static bool core_set_max_freq_ratio(void) +{ + u64 base_freq, turbo_freq; + int err; + + err = rdmsrl_safe(MSR_PLATFORM_INFO, &base_freq); + if (err) + return false; + + err = rdmsrl_safe(MSR_TURBO_RATIO_LIMIT, &turbo_freq); + if (err) + return false; + + base_freq = (base_freq >> 8) & 0xFF; /* max P state */ + turbo_freq = (turbo_freq >> 24) & 0xFF; /* 4C turbo */ + + arch_max_freq_ratio = div_u64(turbo_freq * SCHED_CAPACITY_SCALE, + base_freq); + return true; +} + +static bool intel_set_max_freq_ratio(void) +{ + /* + * TODO: add support for: + * + * - Xeon Gold/Platinum + * - Xeon Phi (KNM, KNL) + * - Atom Goldmont + * - Atom Silvermont + */ + + if (x86_match_cpu(has_skx_turbo_ratio_limits) || + x86_match_cpu(has_knl_turbo_ratio_limits) || + x86_match_cpu(has_glm_turbo_ratio_limits)) + return false; + + if (turbo_disabled() || core_set_max_freq_ratio()) + return true; + + return false; +} + +static void init_counter_refs(void *arg) +{ + u64 aperf, mperf; + + rdmsrl(MSR_IA32_APERF, aperf); + rdmsrl(MSR_IA32_MPERF, mperf); + + this_cpu_write(arch_prev_aperf, aperf); + this_cpu_write(arch_prev_mperf, mperf); +} + +static void init_freq_invariance(void) +{ + bool ret = false; + + if (smp_processor_id() != 0 || !boot_cpu_has(X86_FEATURE_APERFMPERF)) + return; + + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) + ret = intel_set_max_freq_ratio(); + + if (ret) { + on_each_cpu(init_counter_refs, NULL, 1); + static_branch_enable(&arch_scale_freq_key); + } else { + pr_debug("Couldn't determine max cpu frequency, necessary for scale-invariant accounting.\n"); + } +} + +DEFINE_PER_CPU(unsigned long, arch_freq_scale) = SCHED_CAPACITY_SCALE; + +void arch_scale_freq_tick(void) +{ + u64 freq_scale; + u64 aperf, mperf; + u64 acnt, mcnt; + + if (!arch_scale_freq_invariant()) + return; + + rdmsrl(MSR_IA32_APERF, aperf); + rdmsrl(MSR_IA32_MPERF, mperf); + + acnt = aperf - this_cpu_read(arch_prev_aperf); + mcnt = mperf - this_cpu_read(arch_prev_mperf); + if (!mcnt) + return; + + this_cpu_write(arch_prev_aperf, aperf); + this_cpu_write(arch_prev_mperf, mperf); + + acnt <<= 2*SCHED_CAPACITY_SHIFT; + mcnt *= arch_max_freq_ratio; + + freq_scale = div64_u64(acnt, mcnt); + + if (freq_scale > SCHED_CAPACITY_SCALE) + freq_scale = SCHED_CAPACITY_SCALE; + + this_cpu_write(arch_freq_scale, freq_scale); +} diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 89e54f3ed571..45f79bcc3146 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3600,6 +3600,7 @@ void scheduler_tick(void) struct task_struct *curr = rq->curr; struct rq_flags rf; + arch_scale_freq_tick(); sched_clock_tick(); rq_lock(rq, &rf); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 1a88dc8ad11b..0844e81964e5 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1968,6 +1968,13 @@ static inline int hrtick_enabled(struct rq *rq) #endif /* CONFIG_SCHED_HRTICK */ +#ifndef arch_scale_freq_tick +static __always_inline +void arch_scale_freq_tick(void) +{ +} +#endif + #ifndef arch_scale_freq_capacity static __always_inline unsigned long arch_scale_freq_capacity(int cpu) From 2a0abc59699896f03bf6f16efb8a3a490511216f Mon Sep 17 00:00:00 2001 From: Giovanni Gherdovich Date: Wed, 22 Jan 2020 16:16:13 +0100 Subject: [PATCH 0002/2231] x86, sched: Add support for frequency invariance on SKYLAKE_X The scheduler needs the ratio freq_curr/freq_max for frequency-invariant accounting. On SKYLAKE_X CPUs set freq_max to the highest frequency that can be sustained by a group of at least 4 cores. From the changelog of commit 31e07522be56 ("tools/power turbostat: fix decoding for GLM, DNV, SKX turbo-ratio limits"): > Newer processors do not hard-code the the number of cpus in each bin > to {1, 2, 3, 4, 5, 6, 7, 8} Rather, they can specify any number > of CPUS in each of the 8 bins: > > eg. > > ... > 37 * 100.0 = 3600.0 MHz max turbo 4 active cores > 38 * 100.0 = 3700.0 MHz max turbo 3 active cores > 39 * 100.0 = 3800.0 MHz max turbo 2 active cores > 39 * 100.0 = 3900.0 MHz max turbo 1 active cores > > could now look something like this: > > ... > 37 * 100.0 = 3600.0 MHz max turbo 16 active cores > 38 * 100.0 = 3700.0 MHz max turbo 8 active cores > 39 * 100.0 = 3800.0 MHz max turbo 4 active cores > 39 * 100.0 = 3900.0 MHz max turbo 2 active cores This encoding of turbo levels applies to both SKYLAKE_X and GOLDMONT/GOLDMONT_D, but we treat these two classes in separate commits because their freq_max values need to be different. For SKX we prefer a lower freq_max in the ratio freq_curr/freq_max, allowing load and utilization to overshoot and the schedutil governor to be more performance-oriented. Models from the Atom series (such as GOLDMONT*) are handled in a forthcoming commit as they have to favor power-efficiency over performance. Results from a performance evaluation follow. 1. TEST SETUP 2. NEUTRAL BENCHMARKS 3. NON-NEUTRAL BENCHMARKS 4. DETAILED TABLES 1. TEST SETUP ------------- Test machine: CPU Model : Intel Xeon Platinum 8260L CPU @ 2.40GHz (a.k.a. Cascade Lake) Fam/Mod/Ste : 6:85:6 Topology : 2 sockets, 24 cores / 48 threads each socket Memory : 192G Storage : SSD, XFS filesystem Max EFFICiency, BASE frequency and available turbo levels (MHz): EFFIC 1000 |********** BASE 2400 |************************ 24C 3100 |******************************* 20C 3300 |********************************* 16C 3600 |************************************ 12C 3600 |************************************ 8C 3600 |************************************ 4C 3700 |************************************* 2C 3900 |*************************************** Tested kernels: Baseline : v5.2, intel_pstate passive, schedutil Comparison #1 : v5.2, intel_pstate active , powersave+HWP Comparison #2 : v5.2, this patch, intel_pstate passive, schedutil 2. NEUTRAL BENCHMARKS --------------------- * pgbench read/write * NASA Parallel Benchmarks (NPB), MPI or OpenMP for message-passing * hackbench * netperf 3. NON-NEUTRAL BENCHMARKS ------------------------- comparison ratio with baseline; 1.00 means neutral, higher is better: I_PSTATE FREQ-INV ---------------------------------------- pgbench read-only 1.10 ~ tbench 1.82 1.14 comparison ratio with baseline; 1.00 means neutral, lower is better: I_PSTATE FREQ-INV ---------------------------------------- dbench ~ 0.97 kernbench 0.88 0.78 gitsource[*] ~ 0.46 [*] "gitsource" consists in running git's unit tests tilde (~) means 1.00, ie result identical to baseline Performance per watt: performance-per-watt ratios with baseline; 1.00 means neutral, higher is better: I_PSTATE FREQ-INV ---------------------------------------- dbench 0.92 0.91 tbench 1.26 1.04 kernbench 0.95 0.96 gitsource 1.03 1.30 Similarly to earlier Xeons, measurable performance gains over non-invariant schedutil are observed on dbench, tbench, kernel compilation and running the git unit tests suite. Looking at the detailed tables show that the patch scores the largest difference when the machine is lightly loaded. Power efficiency suffers lightly on kernbench and a bit more on dbench, but largely improves on gitsource (which also runs considerably faster). For reference, we also report results using active intel_pstate with powersave and HWP; the largest gap between non-invariant schedutil and intel_pstate+powersave is still tbench, which runs 82% better and with 26% improved efficiency on the latter configuration -- this divide isn't closed yet by frequency-invariant schedutil. 4. DETAILED TABLES ------------------ Benchmark : tbench4 (i.e. dbench4 over the network, actually loopback) Varying parameter : number of clients Unit : MB/sec (higher is better) 5.2.0 vanilla (BASELINE) 5.2.0 intel_pstate/HWP 5.2.0 freq-inv - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Hmean 1 183.56 +- 0.21% ( ) 516.12 +- 0.57% ( 181.18%) 185.59 +- 0.59% ( 1.11%) Hmean 2 365.75 +- 0.25% ( ) 1015.14 +- 0.33% ( 177.55%) 402.59 +- 4.48% ( 10.07%) Hmean 4 720.99 +- 0.44% ( ) 1951.75 +- 0.28% ( 170.70%) 738.39 +- 1.72% ( 2.41%) Hmean 8 1449.93 +- 0.34% ( ) 3830.56 +- 0.24% ( 164.19%) 1750.36 +- 4.65% ( 20.72%) Hmean 16 2874.26 +- 0.57% ( ) 7381.62 +- 0.53% ( 156.82%) 4348.35 +- 2.22% ( 51.29%) Hmean 32 6116.17 +- 5.10% ( ) 13013.05 +- 0.08% ( 112.76%) 8980.35 +- 0.66% ( 46.83%) Hmean 64 14485.04 +- 3.46% ( ) 17835.12 +- 0.35% ( 23.13%) 16540.73 +- 0.51% ( 14.19%) Hmean 128 30779.16 +- 3.20% ( ) 32796.94 +- 2.13% ( 6.56%) 31512.58 +- 0.20% ( 2.38%) Hmean 256 34664.66 +- 0.81% ( ) 34604.67 +- 0.46% ( -0.17%) 34943.70 +- 0.25% ( 0.80%) Hmean 384 33957.51 +- 0.11% ( ) 34091.50 +- 0.14% ( 0.39%) 33921.41 +- 0.09% ( -0.11%) Benchmark : kernbench (kernel compilation) Varying parameter : number of jobs Unit : seconds (lower is better) 5.2.0 vanilla (BASELINE) 5.2.0 intel_pstate/HWP 5.2.0 freq-inv - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Amean 2 332.94 +- 0.40% ( ) 260.16 +- 0.45% ( 21.86%) 233.56 +- 0.21% ( 29.85%) Amean 4 173.04 +- 0.43% ( ) 138.76 +- 0.03% ( 19.81%) 123.59 +- 0.11% ( 28.58%) Amean 8 89.65 +- 0.20% ( ) 73.54 +- 0.09% ( 17.97%) 65.69 +- 0.10% ( 26.72%) Amean 16 48.08 +- 1.41% ( ) 41.64 +- 1.61% ( 13.40%) 36.00 +- 1.80% ( 25.11%) Amean 32 28.78 +- 0.72% ( ) 26.61 +- 1.99% ( 7.55%) 23.19 +- 1.68% ( 19.43%) Amean 64 20.46 +- 1.85% ( ) 19.76 +- 0.35% ( 3.42%) 17.38 +- 0.92% ( 15.06%) Amean 128 18.69 +- 1.70% ( ) 17.59 +- 1.04% ( 5.90%) 15.73 +- 1.40% ( 15.85%) Amean 192 18.82 +- 1.01% ( ) 17.76 +- 0.77% ( 5.67%) 15.57 +- 1.80% ( 17.28%) Benchmark : gitsource (time to run the git unit test suite) Varying parameter : none Unit : seconds (lower is better) 5.2.0 vanilla (BASELINE) 5.2.0 intel_pstate/HWP 5.2.0 freq-inv - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Amean 792.49 +- 0.20% ( ) 779.35 +- 0.24% ( 1.66%) 427.14 +- 0.16% ( 46.10%) Signed-off-by: Giovanni Gherdovich Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Rafael J. Wysocki Link: https://lkml.kernel.org/r/20200122151617.531-3-ggherdovich@suse.cz --- arch/x86/kernel/smpboot.c | 66 +++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 28696bccf912..ba9d3bdc191c 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1841,24 +1841,52 @@ static const struct x86_cpu_id has_glm_turbo_ratio_limits[] = { {} }; -static bool core_set_max_freq_ratio(void) +static bool skx_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq, int size) +{ + u64 ratios, counts; + u32 group_size; + int err, i; + + err = rdmsrl_safe(MSR_PLATFORM_INFO, base_freq); + if (err) + return false; + + *base_freq = (*base_freq >> 8) & 0xFF; /* max P state */ + + err = rdmsrl_safe(MSR_TURBO_RATIO_LIMIT, &ratios); + if (err) + return false; + + err = rdmsrl_safe(MSR_TURBO_RATIO_LIMIT1, &counts); + if (err) + return false; + + for (i = 0; i < 64; i += 8) { + group_size = (counts >> i) & 0xFF; + if (group_size >= size) { + *turbo_freq = (ratios >> i) & 0xFF; + return true; + } + } + + return false; +} + +static bool core_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq) { - u64 base_freq, turbo_freq; int err; - err = rdmsrl_safe(MSR_PLATFORM_INFO, &base_freq); + err = rdmsrl_safe(MSR_PLATFORM_INFO, base_freq); if (err) return false; - err = rdmsrl_safe(MSR_TURBO_RATIO_LIMIT, &turbo_freq); + err = rdmsrl_safe(MSR_TURBO_RATIO_LIMIT, turbo_freq); if (err) return false; - base_freq = (base_freq >> 8) & 0xFF; /* max P state */ - turbo_freq = (turbo_freq >> 24) & 0xFF; /* 4C turbo */ + *base_freq = (*base_freq >> 8) & 0xFF; /* max P state */ + *turbo_freq = (*turbo_freq >> 24) & 0xFF; /* 4C turbo */ - arch_max_freq_ratio = div_u64(turbo_freq * SCHED_CAPACITY_SCALE, - base_freq); return true; } @@ -1867,21 +1895,33 @@ static bool intel_set_max_freq_ratio(void) /* * TODO: add support for: * - * - Xeon Gold/Platinum * - Xeon Phi (KNM, KNL) * - Atom Goldmont * - Atom Silvermont */ - if (x86_match_cpu(has_skx_turbo_ratio_limits) || - x86_match_cpu(has_knl_turbo_ratio_limits) || + u64 base_freq = 1, turbo_freq = 1; + + if (x86_match_cpu(has_knl_turbo_ratio_limits) || x86_match_cpu(has_glm_turbo_ratio_limits)) return false; - if (turbo_disabled() || core_set_max_freq_ratio()) - return true; + if (turbo_disabled()) + goto out; + + if (x86_match_cpu(has_skx_turbo_ratio_limits) && + skx_set_max_freq_ratio(&base_freq, &turbo_freq, 4)) + goto out; + + if (core_set_max_freq_ratio(&base_freq, &turbo_freq)) + goto out; return false; + +out: + arch_max_freq_ratio = div_u64(turbo_freq * SCHED_CAPACITY_SCALE, + base_freq); + return true; } static void init_counter_refs(void *arg) From 8bea0dfb4a820ae063568a87cc2e7d8f587377af Mon Sep 17 00:00:00 2001 From: Giovanni Gherdovich Date: Wed, 22 Jan 2020 16:16:14 +0100 Subject: [PATCH 0003/2231] x86, sched: Add support for frequency invariance on XEON_PHI_KNL/KNM The scheduler needs the ratio freq_curr/freq_max for frequency-invariant accounting. On Xeon Phi CPUs set freq_max to the second-highest frequency reported by the CPU. Xeon Phi CPUs such as Knights Landing and Knights Mill typically have either one or two turbo frequencies; in the former case that's 100 MHz above the base frequency, in the latter case the two levels are 100 MHz and 200 MHz above base frequency. We set freq_max to the second-highest frequency reported by the CPU. This could be the base frequency (if only one turbo level is available) or the first turbo level (if two levels are available). The rationale is to compromise between power efficiency or performance -- going straight to max turbo would favor efficiency and blindly using base freq would favor performance. For reference, this is how MSR_TURBO_RATIO_LIMIT must be parsed on a Xeon Phi to get the available frequencies (taken from a comment in turbostat's sources): [0] -- Reserved [7:1] -- Base value of number of active cores of bucket 1. [15:8] -- Base value of freq ratio of bucket 1. [20:16] -- +ve delta of number of active cores of bucket 2. i.e. active cores of bucket 2 = active cores of bucket 1 + delta [23:21] -- Negative delta of freq ratio of bucket 2. i.e. freq ratio of bucket 2 = freq ratio of bucket 1 - delta [28:24]-- +ve delta of number of active cores of bucket 3. [31:29]-- -ve delta of freq ratio of bucket 3. [36:32]-- +ve delta of number of active cores of bucket 4. [39:37]-- -ve delta of freq ratio of bucket 4. [44:40]-- +ve delta of number of active cores of bucket 5. [47:45]-- -ve delta of freq ratio of bucket 5. [52:48]-- +ve delta of number of active cores of bucket 6. [55:53]-- -ve delta of freq ratio of bucket 6. [60:56]-- +ve delta of number of active cores of bucket 7. [63:61]-- -ve delta of freq ratio of bucket 7. 1. PERFORMANCE EVALUATION: TBENCH +5% 2. NEUTRAL BENCHMARKS (ALL OTHERS) 3. TEST SETUP 1. PERFORMANCE EVALUATION: TBENCH +5% ------------------------------------- A performance evaluation was conducted on a Knights Mill machine (see "Test Setup" below), were the frequency-invariance patch (on schedutil) is compared to both non-invariant schedutil and active intel_pstate with powersave: all three tested kernels behave the same performance-wise and with regard to power consumption (performance per watt). The only notable difference is tbench: comparison ratio of performance with baseline; 1.00 means neutral, higher is better: I_PSTATE FREQ-INV ---------------------------------------- tbench 1.04 1.05 performance-per-watt ratios with baseline; 1.00 means neutral, higher is better: I_PSTATE FREQ-INV ---------------------------------------- tbench 1.03 1.04 which essentially means that frequency-invariant schedutil is 5% better than baseline, the same as intel_pstate+powersave. As the results above are averaged over the varying parameter, here the detailed table. Varying parameter : number of clients Unit : MB/sec (higher is better) 5.2.0 vanilla (BASELINE) 5.2.0 intel_pstate 5.2.0 freq-inv - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Hmean 1 49.06 +- 2.12% ( ) 51.66 +- 1.52% ( 5.30%) 52.87 +- 0.88% ( 7.76%) Hmean 2 93.82 +- 0.45% ( ) 103.24 +- 0.70% ( 10.05%) 105.90 +- 0.70% ( 12.88%) Hmean 4 192.46 +- 1.15% ( ) 215.95 +- 0.60% ( 12.21%) 215.78 +- 1.43% ( 12.12%) Hmean 8 406.74 +- 2.58% ( ) 438.58 +- 0.36% ( 7.83%) 437.61 +- 0.97% ( 7.59%) Hmean 16 857.70 +- 1.22% ( ) 890.26 +- 0.72% ( 3.80%) 889.11 +- 0.73% ( 3.66%) Hmean 32 1760.10 +- 0.92% ( ) 1791.70 +- 0.44% ( 1.79%) 1787.95 +- 0.44% ( 1.58%) Hmean 64 3183.50 +- 0.34% ( ) 3183.19 +- 0.36% ( -0.01%) 3187.53 +- 0.36% ( 0.13%) Hmean 128 4830.96 +- 0.31% ( ) 4846.53 +- 0.30% ( 0.32%) 4855.86 +- 0.30% ( 0.52%) Hmean 256 5467.98 +- 0.38% ( ) 5793.80 +- 0.28% ( 5.96%) 5821.94 +- 0.17% ( 6.47%) Hmean 512 5398.10 +- 0.06% ( ) 5745.56 +- 0.08% ( 6.44%) 5503.68 +- 0.07% ( 1.96%) Hmean 1024 5290.43 +- 0.63% ( ) 5221.07 +- 0.47% ( -1.31%) 5277.22 +- 0.80% ( -0.25%) Hmean 1088 5139.71 +- 0.57% ( ) 5236.02 +- 0.71% ( 1.87%) 5190.57 +- 0.41% ( 0.99%) 2. NEUTRAL BENCHMARKS (ALL OTHERS) ---------------------------------- * pgbench (both read/write and read-only) * NASA Parallel Benchmarks (NPB), MPI or OpenMP for message-passing * hackbench * netperf * dbench * kernbench * gitsource (git unit test suite) 3. TEST SETUP ------------- Test machine: CPU Model : Intel Xeon Phi CPU 7255 @ 1.10GHz (a.k.a. Knights Mill) Fam/Mod/Ste : 6:133:0 Topology : 1 socket, 68 cores / 272 threads Memory : 96G Storage : rotary, XFS filesystem Max EFFICiency, BASE frequency and available turbo levels (MHz): EFFIC 1000 |********** BASE 1100 |*********** 68C 1100 |*********** 30C 1200 |************ Tested kernels: Baseline : v5.2, intel_pstate passive, schedutil Comparison #1 : v5.2, intel_pstate active , powersave Comparison #2 : v5.2, this patch, intel_pstate passive, schedutil Signed-off-by: Giovanni Gherdovich Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Rafael J. Wysocki Link: https://lkml.kernel.org/r/20200122151617.531-4-ggherdovich@suse.cz --- arch/x86/kernel/smpboot.c | 49 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index ba9d3bdc191c..8cb3113377a9 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1841,6 +1841,48 @@ static const struct x86_cpu_id has_glm_turbo_ratio_limits[] = { {} }; +static bool knl_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq, + int num_delta_fratio) +{ + int fratio, delta_fratio, found; + int err, i; + u64 msr; + + if (!x86_match_cpu(has_knl_turbo_ratio_limits)) + return false; + + err = rdmsrl_safe(MSR_PLATFORM_INFO, base_freq); + if (err) + return false; + + *base_freq = (*base_freq >> 8) & 0xFF; /* max P state */ + + err = rdmsrl_safe(MSR_TURBO_RATIO_LIMIT, &msr); + if (err) + return false; + + fratio = (msr >> 8) & 0xFF; + i = 16; + found = 0; + do { + if (found >= num_delta_fratio) { + *turbo_freq = fratio; + return true; + } + + delta_fratio = (msr >> (i + 5)) & 0x7; + + if (delta_fratio) { + found += 1; + fratio -= delta_fratio; + } + + i += 8; + } while (i < 64); + + return true; +} + static bool skx_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq, int size) { u64 ratios, counts; @@ -1895,20 +1937,21 @@ static bool intel_set_max_freq_ratio(void) /* * TODO: add support for: * - * - Xeon Phi (KNM, KNL) * - Atom Goldmont * - Atom Silvermont */ u64 base_freq = 1, turbo_freq = 1; - if (x86_match_cpu(has_knl_turbo_ratio_limits) || - x86_match_cpu(has_glm_turbo_ratio_limits)) + if (x86_match_cpu(has_glm_turbo_ratio_limits)) return false; if (turbo_disabled()) goto out; + if (knl_set_max_freq_ratio(&base_freq, &turbo_freq, 1)) + goto out; + if (x86_match_cpu(has_skx_turbo_ratio_limits) && skx_set_max_freq_ratio(&base_freq, &turbo_freq, 4)) goto out; From eacf0474aec8bdccdc7f19386319127c67be3588 Mon Sep 17 00:00:00 2001 From: Giovanni Gherdovich Date: Wed, 22 Jan 2020 16:16:15 +0100 Subject: [PATCH 0004/2231] x86, sched: Add support for frequency invariance on ATOM_GOLDMONT* The scheduler needs the ratio freq_curr/freq_max for frequency-invariant accounting. On GOLDMONT (aka Apollo Lake), GOLDMONT_D (aka Denverton) and GOLDMONT_PLUS CPUs (aka Gemini Lake) set freq_max to the highest frequency reported by the CPU. The encoding of turbo ratios for GOLDMONT* is identical to the one for SKYLAKE_X, but we treat the Atom case apart because we want to set freq_max to a higher value, thus the ratio freq_curr/freq_max to be lower, leading to more conservative frequency selections (favoring power efficiency). Signed-off-by: Giovanni Gherdovich Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Rafael J. Wysocki Link: https://lkml.kernel.org/r/20200122151617.531-5-ggherdovich@suse.cz --- arch/x86/kernel/smpboot.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 8cb3113377a9..3e32d620f1fb 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1795,6 +1795,10 @@ void native_play_dead(void) * which would ignore the entire turbo range (a conspicuous part, making * freq_curr/freq_max always maxed out). * + * An exception to the heuristic above is the Atom uarch, where we choose the + * highest turbo level for freq_max since Atom's are generally oriented towards + * power efficiency. + * * Setting freq_max to anything less than the 1C turbo ratio makes the ratio * freq_curr / freq_max to eventually grow >1, in which case we clip it to 1. */ @@ -1937,18 +1941,18 @@ static bool intel_set_max_freq_ratio(void) /* * TODO: add support for: * - * - Atom Goldmont * - Atom Silvermont */ u64 base_freq = 1, turbo_freq = 1; - if (x86_match_cpu(has_glm_turbo_ratio_limits)) - return false; - if (turbo_disabled()) goto out; + if (x86_match_cpu(has_glm_turbo_ratio_limits) && + skx_set_max_freq_ratio(&base_freq, &turbo_freq, 1)) + goto out; + if (knl_set_max_freq_ratio(&base_freq, &turbo_freq, 1)) goto out; From 298c6f99bf30ef735e79f7f6d086bdfae505d380 Mon Sep 17 00:00:00 2001 From: Giovanni Gherdovich Date: Wed, 22 Jan 2020 16:16:16 +0100 Subject: [PATCH 0005/2231] x86, sched: Add support for frequency invariance on ATOM The scheduler needs the ratio freq_curr/freq_max for frequency-invariant accounting. On all ATOM CPUs prior to Goldmont, set freq_max to the 1-core turbo ratio. We intended to perform tests validating that this patch doesn't regress in terms of energy efficiency, given that this is the primary concern on Atom processors. Alas, we found out that turbostat doesn't support reading RAPL interfaces on our test machine (Airmont), and we don't have external equipment to measure power consumption; all we have is the performance results of the benchmarks we ran. Test machine: Platform : Dell Wyse 3040 Thin Client[1] CPU Model : Intel Atom x5-Z8350 (aka Cherry Trail, aka Airmont) Fam/Mod/Ste : 6:76:4 Topology : 1 socket, 4 cores / 4 threads Memory : 2G Storage : onboard flash, XFS filesystem [1] https://www.dell.com/en-us/work/shop/wyse-endpoints-and-software/wyse-3040-thin-client/spd/wyse-3040-thin-client Base frequency and available turbo levels (MHz): Min Operating Freq 266 |*** Low Freq Mode 800 |******** Base Freq 2400 |************************ 4 Cores 2800 |**************************** 3 Cores 2800 |**************************** 2 Cores 3200 |******************************** 1 Core 3200 |******************************** Tested kernels: Baseline : v5.4-rc1, intel_pstate passive, schedutil Comparison #1 : v5.4-rc1, intel_pstate active , powersave Comparison #2 : v5.4-rc1, this patch, intel_pstate passive, schedutil tbench, hackbench and kernbench performed the same under all three kernels; dbench ran faster with intel_pstate/powersave and the git unit tests were a lot faster with intel_pstate/powersave and invariant schedutil wrt the baseline. Not that any of this is terrbily interesting anyway, one doesn't buy an Atom system to go fast. Power consumption regressions aren't expected but we lack the equipment to make that measurement. Turbostat seems to think that reading RAPL on this machine isn't a good idea and we're trusting that decision. comparison ratio of performance with baseline; 1.00 means neutral, lower is better: I_PSTATE FREQ-INV ---------------------------------------- dbench 0.90 ~ kernbench 0.98 0.97 gitsource 0.63 0.43 Signed-off-by: Giovanni Gherdovich Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Rafael J. Wysocki Link: https://lkml.kernel.org/r/20200122151617.531-6-ggherdovich@suse.cz --- arch/x86/kernel/smpboot.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 3e32d620f1fb..5f04bf8419f9 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1821,6 +1821,24 @@ static bool turbo_disabled(void) return (misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE); } +static bool slv_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq) +{ + int err; + + err = rdmsrl_safe(MSR_ATOM_CORE_RATIOS, base_freq); + if (err) + return false; + + err = rdmsrl_safe(MSR_ATOM_CORE_TURBO_RATIOS, turbo_freq); + if (err) + return false; + + *base_freq = (*base_freq >> 16) & 0x3F; /* max P state */ + *turbo_freq = *turbo_freq & 0x3F; /* 1C turbo */ + + return true; +} + #include #include @@ -1938,17 +1956,14 @@ static bool core_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq) static bool intel_set_max_freq_ratio(void) { - /* - * TODO: add support for: - * - * - Atom Silvermont - */ - u64 base_freq = 1, turbo_freq = 1; if (turbo_disabled()) goto out; + if (slv_set_max_freq_ratio(&base_freq, &turbo_freq)) + goto out; + if (x86_match_cpu(has_glm_turbo_ratio_limits) && skx_set_max_freq_ratio(&base_freq, &turbo_freq, 1)) goto out; From 918229cdd5abb50d8a2edfcd8dc6b6bc53afd765 Mon Sep 17 00:00:00 2001 From: Giovanni Gherdovich Date: Wed, 22 Jan 2020 16:16:17 +0100 Subject: [PATCH 0006/2231] x86/intel_pstate: Handle runtime turbo disablement/enablement in frequency invariance On some platforms such as the Dell XPS 13 laptop the firmware disables turbo when the machine is disconnected from AC, and viceversa it enables it again when it's reconnected. In these cases a _PPC ACPI notification is issued. The scheduler needs to know freq_max for frequency-invariant calculations. To account for turbo availability to come and go, record freq_max at boot as if turbo was available and store it in a helper variable. Use a setter function to swap between freq_base and freq_max every time turbo goes off or on. Signed-off-by: Giovanni Gherdovich Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Rafael J. Wysocki Link: https://lkml.kernel.org/r/20200122151617.531-7-ggherdovich@suse.cz --- arch/x86/include/asm/topology.h | 5 +++++ arch/x86/kernel/smpboot.c | 15 ++++++++++----- drivers/cpufreq/intel_pstate.c | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index 2ebf7b7b2126..79d8d5496330 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -211,6 +211,11 @@ static inline long arch_scale_freq_capacity(int cpu) extern void arch_scale_freq_tick(void); #define arch_scale_freq_tick arch_scale_freq_tick +extern void arch_set_max_freq_ratio(bool turbo_disabled); +#else +static inline void arch_set_max_freq_ratio(bool turbo_disabled) +{ +} #endif #endif /* _ASM_X86_TOPOLOGY_H */ diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 5f04bf8419f9..467191e51196 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1807,8 +1807,15 @@ DEFINE_STATIC_KEY_FALSE(arch_scale_freq_key); static DEFINE_PER_CPU(u64, arch_prev_aperf); static DEFINE_PER_CPU(u64, arch_prev_mperf); +static u64 arch_turbo_freq_ratio = SCHED_CAPACITY_SCALE; static u64 arch_max_freq_ratio = SCHED_CAPACITY_SCALE; +void arch_set_max_freq_ratio(bool turbo_disabled) +{ + arch_max_freq_ratio = turbo_disabled ? SCHED_CAPACITY_SCALE : + arch_turbo_freq_ratio; +} + static bool turbo_disabled(void) { u64 misc_en; @@ -1956,10 +1963,7 @@ static bool core_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq) static bool intel_set_max_freq_ratio(void) { - u64 base_freq = 1, turbo_freq = 1; - - if (turbo_disabled()) - goto out; + u64 base_freq, turbo_freq; if (slv_set_max_freq_ratio(&base_freq, &turbo_freq)) goto out; @@ -1981,8 +1985,9 @@ static bool intel_set_max_freq_ratio(void) return false; out: - arch_max_freq_ratio = div_u64(turbo_freq * SCHED_CAPACITY_SCALE, + arch_turbo_freq_ratio = div_u64(turbo_freq * SCHED_CAPACITY_SCALE, base_freq); + arch_set_max_freq_ratio(turbo_disabled()); return true; } diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index d2fa3e9ccd97..abbeeca8bb3b 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -922,6 +922,7 @@ static void intel_pstate_update_limits(unsigned int cpu) */ if (global.turbo_disabled_mf != global.turbo_disabled) { global.turbo_disabled_mf = global.turbo_disabled; + arch_set_max_freq_ratio(global.turbo_disabled); for_each_possible_cpu(cpu) intel_pstate_update_max_freq(cpu); } else { From bec2860a2bd6cd38ea34434d04f4033eb32f0f31 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Fri, 6 Dec 2019 22:54:22 +0530 Subject: [PATCH 0007/2231] sched/fair: Optimize select_idle_core() Currently we loop through all threads of a core to evaluate if the core is idle or not. This is unnecessary. If a thread of a core is not idle, skip evaluating other threads of a core. Also while clearing the cpumask, bits of all CPUs of a core can be cleared in one-shot. Collecting ticks on a Power 9 SMT 8 system around select_idle_core while running schbench shows us (units are in ticks, hence lesser is better) Without patch N Min Max Median Avg Stddev x 130 151 1083 284 322.72308 144.41494 With patch N Min Max Median Avg Stddev Improvement x 164 88 610 201 225.79268 106.78943 30.03% Signed-off-by: Srikar Dronamraju Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Valentin Schneider Reviewed-by: Vincent Guittot Acked-by: Mel Gorman Link: https://lkml.kernel.org/r/20191206172422.6578-1-srikar@linux.vnet.ibm.com --- kernel/sched/fair.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 25dffc03f0f6..1a0ce83e835a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5787,10 +5787,12 @@ static int select_idle_core(struct task_struct *p, struct sched_domain *sd, int bool idle = true; for_each_cpu(cpu, cpu_smt_mask(core)) { - __cpumask_clear_cpu(cpu, cpus); - if (!available_idle_cpu(cpu)) + if (!available_idle_cpu(cpu)) { idle = false; + break; + } } + cpumask_andnot(cpus, cpus, cpu_smt_mask(core)); if (idle) return core; From b4fb015eeff7f3e5518a7dbe8061169a3e2f2bc7 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Sat, 25 Jan 2020 17:50:38 +0300 Subject: [PATCH 0008/2231] sched/rt: Optimize checking group RT scheduler constraints Group RT scheduler contains protection against setting zero runtime for cgroup with RT tasks. Right now function tg_set_rt_bandwidth() iterates over all CPU cgroups and calls tg_has_rt_tasks() for any cgroup which runtime is zero (not only for changed one). Default RT runtime is zero, thus tg_has_rt_tasks() will is called for almost at CPU cgroups. This protection already is slightly racy: runtime limit could be changed between cpu_cgroup_can_attach() and cpu_cgroup_attach() because changing cgroup attribute does not lock cgroup_mutex while attach does not lock rt_constraints_mutex. Changing task scheduler class also races with changing rt runtime: check in __sched_setscheduler() isn't protected. Function tg_has_rt_tasks() iterates over all threads in the system. This gives NR_CGROUPS * NR_TASKS operations under single tasklist_lock locked for read tg_set_rt_bandwidth(). Any concurrent attempt of locking tasklist_lock for write (for example fork) will stuck with disabled irqs. This patch makes two optimizations: 1) Remove locking tasklist_lock and iterate only tasks in cgroup 2) Call tg_has_rt_tasks() iff rt runtime changes from non-zero to zero All changed code is under CONFIG_RT_GROUP_SCHED. Testcase: # mkdir /sys/fs/cgroup/cpu/test{1..10000} # echo 0 | tee /sys/fs/cgroup/cpu/test*/cpu.rt_runtime_us At the same time without patch fork time will be >100ms: # perf trace -e clone --duration 100 stress-ng --fork 1 Also remote ping will show timings >100ms caused by irq latency. Signed-off-by: Konstantin Khlebnikov Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/157996383820.4651.11292439232549211693.stgit@buzz --- kernel/sched/rt.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 4043abe45459..55a4a5042292 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -2449,10 +2449,11 @@ const struct sched_class rt_sched_class = { */ static DEFINE_MUTEX(rt_constraints_mutex); -/* Must be called with tasklist_lock held */ static inline int tg_has_rt_tasks(struct task_group *tg) { - struct task_struct *g, *p; + struct task_struct *task; + struct css_task_iter it; + int ret = 0; /* * Autogroups do not have RT tasks; see autogroup_create(). @@ -2460,12 +2461,12 @@ static inline int tg_has_rt_tasks(struct task_group *tg) if (task_group_is_autogroup(tg)) return 0; - for_each_process_thread(g, p) { - if (rt_task(p) && task_group(p) == tg) - return 1; - } + css_task_iter_start(&tg->css, 0, &it); + while (!ret && (task = css_task_iter_next(&it))) + ret |= rt_task(task); + css_task_iter_end(&it); - return 0; + return ret; } struct rt_schedulable_data { @@ -2496,9 +2497,10 @@ static int tg_rt_schedulable(struct task_group *tg, void *data) return -EINVAL; /* - * Ensure we don't starve existing RT tasks. + * Ensure we don't starve existing RT tasks if runtime turns zero. */ - if (rt_bandwidth_enabled() && !runtime && tg_has_rt_tasks(tg)) + if (rt_bandwidth_enabled() && !runtime && + tg->rt_bandwidth.rt_runtime && tg_has_rt_tasks(tg)) return -EBUSY; total = to_ratio(period, runtime); @@ -2564,7 +2566,6 @@ static int tg_set_rt_bandwidth(struct task_group *tg, return -EINVAL; mutex_lock(&rt_constraints_mutex); - read_lock(&tasklist_lock); err = __rt_schedulable(tg, rt_period, rt_runtime); if (err) goto unlock; @@ -2582,7 +2583,6 @@ static int tg_set_rt_bandwidth(struct task_group *tg, } raw_spin_unlock_irq(&tg->rt_bandwidth.rt_runtime_lock); unlock: - read_unlock(&tasklist_lock); mutex_unlock(&rt_constraints_mutex); return err; @@ -2641,9 +2641,7 @@ static int sched_rt_global_constraints(void) int ret = 0; mutex_lock(&rt_constraints_mutex); - read_lock(&tasklist_lock); ret = __rt_schedulable(NULL, 0, 0); - read_unlock(&tasklist_lock); mutex_unlock(&rt_constraints_mutex); return ret; From 3ca2a90d749fa169a07df785d65b1060af36cbbe Mon Sep 17 00:00:00 2001 From: Saurav Girepunje Date: Sat, 25 Jan 2020 19:16:04 +0530 Subject: [PATCH 0009/2231] staging: rtl8723bs: hal: fix condition with no effect fix warning reorted by coccicheck WARNING: possible condition with no effect (if == else) Signed-off-by: Saurav Girepunje Link: https://lore.kernel.org/r/20200125134604.GA4247@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c index 02da0a883594..8dfa9b988e20 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c +++ b/drivers/staging/rtl8723bs/hal/HalBtc8723b2Ant.c @@ -1610,8 +1610,6 @@ static void halbtc8723b2ant_TdmaDurationAdjust( HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(13); else if (maxInterval == 2) HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(14); - else if (maxInterval == 3) - HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(15); else HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(15); } else { @@ -1619,8 +1617,6 @@ static void halbtc8723b2ant_TdmaDurationAdjust( HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(9); else if (maxInterval == 2) HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(10); - else if (maxInterval == 3) - HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(11); else HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(11); } @@ -1630,8 +1626,6 @@ static void halbtc8723b2ant_TdmaDurationAdjust( HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(5); else if (maxInterval == 2) HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(6); - else if (maxInterval == 3) - HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(7); else HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(7); } else { @@ -1639,8 +1633,6 @@ static void halbtc8723b2ant_TdmaDurationAdjust( HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(1); else if (maxInterval == 2) HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(2); - else if (maxInterval == 3) - HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(3); else HAL_BTC8723B2ANT_DMA_DURATION_ADJUST(3); } From a1182cda0c78518d4c391d2b46c3661d88034701 Mon Sep 17 00:00:00 2001 From: Simon Fong Date: Tue, 28 Jan 2020 22:14:26 +0800 Subject: [PATCH 0010/2231] Staging: vt6655: device_main: cleanup long line cleanup a long line coding style warning. Signed-off-by: Simon Fong Link: https://lore.kernel.org/r/20200128141426.12230-1-simon.fongnt@yahoo.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vt6655/device_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index f69fc687d4c3..0442f71494b2 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -133,7 +133,8 @@ static int device_init_td1_ring(struct vnt_private *priv); static int device_rx_srv(struct vnt_private *priv, unsigned int idx); static int device_tx_srv(struct vnt_private *priv, unsigned int idx); static bool device_alloc_rx_buf(struct vnt_private *, struct vnt_rx_desc *); -static void device_free_rx_buf(struct vnt_private *priv, struct vnt_rx_desc *rd); +static void device_free_rx_buf(struct vnt_private *priv, + struct vnt_rx_desc *rd); static void device_init_registers(struct vnt_private *priv); static void device_free_tx_buf(struct vnt_private *, struct vnt_tx_desc *); static void device_free_td0_ring(struct vnt_private *priv); From be9bca8cfbda67ed6e8e44017df5e544881d629d Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 2 Feb 2020 19:46:18 +0000 Subject: [PATCH 0011/2231] staging: vt6656: Disable and remove fall back rates from driver. The fall back rates are not properly implemented in driver and form part of the legacy driver. mac80211 has no indication that this is happening and it does appear the driver does function considerably better without them so remove them. Signed-off-by: Malcolm Priestley Link: https://lore.kernel.org/r/47cc31e5-226b-f84f-3655-51a269735130@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vt6656/int.c | 38 ------------------------------- drivers/staging/vt6656/main_usb.c | 4 ---- drivers/staging/vt6656/rxtx.c | 29 +---------------------- 3 files changed, 1 insertion(+), 70 deletions(-) diff --git a/drivers/staging/vt6656/int.c b/drivers/staging/vt6656/int.c index af215860be4c..3fa61c368464 100644 --- a/drivers/staging/vt6656/int.c +++ b/drivers/staging/vt6656/int.c @@ -23,22 +23,6 @@ #include "power.h" #include "usbpipe.h" -static const u8 fallback_rate0[5][5] = { - {RATE_18M, RATE_18M, RATE_12M, RATE_12M, RATE_12M}, - {RATE_24M, RATE_24M, RATE_18M, RATE_12M, RATE_12M}, - {RATE_36M, RATE_36M, RATE_24M, RATE_18M, RATE_18M}, - {RATE_48M, RATE_48M, RATE_36M, RATE_24M, RATE_24M}, - {RATE_54M, RATE_54M, RATE_48M, RATE_36M, RATE_36M} -}; - -static const u8 fallback_rate1[5][5] = { - {RATE_18M, RATE_18M, RATE_12M, RATE_6M, RATE_6M}, - {RATE_24M, RATE_24M, RATE_18M, RATE_6M, RATE_6M}, - {RATE_36M, RATE_36M, RATE_24M, RATE_12M, RATE_12M}, - {RATE_48M, RATE_48M, RATE_24M, RATE_12M, RATE_12M}, - {RATE_54M, RATE_54M, RATE_36M, RATE_18M, RATE_18M} -}; - int vnt_int_start_interrupt(struct vnt_private *priv) { int ret = 0; @@ -59,7 +43,6 @@ static int vnt_int_report_rate(struct vnt_private *priv, u8 pkt_no, u8 tsr) { struct vnt_usb_send_context *context; struct ieee80211_tx_info *info; - struct ieee80211_rate *rate; u8 tx_retry = (tsr & 0xf0) >> 4; s8 idx; @@ -74,27 +57,6 @@ static int vnt_int_report_rate(struct vnt_private *priv, u8 pkt_no, u8 tsr) info = IEEE80211_SKB_CB(context->skb); idx = info->control.rates[0].idx; - if (context->fb_option && !(tsr & (TSR_TMO | TSR_RETRYTMO))) { - u8 tx_rate; - u8 retry = tx_retry; - - rate = ieee80211_get_tx_rate(priv->hw, info); - tx_rate = rate->hw_value - RATE_18M; - - if (retry > 4) - retry = 4; - - if (context->fb_option == AUTO_FB_0) - tx_rate = fallback_rate0[tx_rate][retry]; - else if (context->fb_option == AUTO_FB_1) - tx_rate = fallback_rate1[tx_rate][retry]; - - if (info->band == NL80211_BAND_5GHZ) - idx = tx_rate - RATE_6M; - else - idx = tx_rate; - } - ieee80211_tx_info_clear_status(info); info->status.rates[0].count = tx_retry; diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 5e48b3ddb94c..17a3daed4401 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -99,7 +99,6 @@ static void vnt_set_options(struct vnt_private *priv) priv->op_mode = NL80211_IFTYPE_UNSPECIFIED; priv->bb_type = BBP_TYPE_DEF; priv->packet_type = priv->bb_type; - priv->auto_fb_ctrl = AUTO_FB_0; priv->preamble_type = 0; priv->exist_sw_net_addr = false; } @@ -261,9 +260,6 @@ static int vnt_init_registers(struct vnt_private *priv) if (ret) goto end; - /* get Auto Fall Back type */ - priv->auto_fb_ctrl = AUTO_FB_0; - /* default Auto Mode */ priv->bb_type = BB_TYPE_11G; diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c index 29caba728906..0fabeb396563 100644 --- a/drivers/staging/vt6656/rxtx.c +++ b/drivers/staging/vt6656/rxtx.c @@ -792,7 +792,7 @@ int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) struct vnt_usb_send_context *tx_context; unsigned long flags; u16 tx_bytes, tx_header_size, tx_body_size, current_rate, duration_id; - u8 pkt_type, fb_option = AUTO_FB_NONE; + u8 pkt_type; bool need_rts = false; bool need_mic = false; @@ -912,33 +912,6 @@ int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) tx_buffer_head->current_rate = cpu_to_le16(current_rate); - /* legacy rates TODO use ieee80211_tx_rate */ - if (current_rate >= RATE_18M && ieee80211_is_data(hdr->frame_control)) { - if (priv->auto_fb_ctrl == AUTO_FB_0) { - tx_buffer_head->fifo_ctl |= - cpu_to_le16(FIFOCTL_AUTO_FB_0); - - priv->tx_rate_fb0 = - vnt_fb_opt0[FB_RATE0][current_rate - RATE_18M]; - priv->tx_rate_fb1 = - vnt_fb_opt0[FB_RATE1][current_rate - RATE_18M]; - - fb_option = AUTO_FB_0; - } else if (priv->auto_fb_ctrl == AUTO_FB_1) { - tx_buffer_head->fifo_ctl |= - cpu_to_le16(FIFOCTL_AUTO_FB_1); - - priv->tx_rate_fb0 = - vnt_fb_opt1[FB_RATE0][current_rate - RATE_18M]; - priv->tx_rate_fb1 = - vnt_fb_opt1[FB_RATE1][current_rate - RATE_18M]; - - fb_option = AUTO_FB_1; - } - } - - tx_context->fb_option = fb_option; - duration_id = vnt_generate_tx_parameter(tx_context, tx_buffer, &mic_hdr, need_mic, need_rts); From 88900631d706af9a958190abaad59da024b93484 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 2 Feb 2020 12:28:07 +0000 Subject: [PATCH 0012/2231] staging: vt6656: Fix return for unsupported cipher modes. mac80211 expect to see -EOPNOTSUPP on unsupported ciphers so these can be done on stack. correct all the returns to do this. Signed-off-by: Malcolm Priestley Link: https://lore.kernel.org/r/b65a3b23-735f-f679-0f16-a54c3266b5ae@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vt6656/key.c | 5 ++++- drivers/staging/vt6656/main_usb.c | 4 +--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/vt6656/key.c b/drivers/staging/vt6656/key.c index dcd933a6b66e..41b73f9670e2 100644 --- a/drivers/staging/vt6656/key.c +++ b/drivers/staging/vt6656/key.c @@ -144,11 +144,14 @@ int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta, break; case WLAN_CIPHER_SUITE_CCMP: if (priv->local_id <= MAC_REVISION_A1) - return -EINVAL; + return -EOPNOTSUPP; key_dec_mode = KEY_CTL_CCMP; key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + break; + default: + return -EOPNOTSUPP; } if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 17a3daed4401..9135aad0863d 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -858,9 +858,7 @@ static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, switch (cmd) { case SET_KEY: - if (vnt_set_keys(hw, sta, vif, key)) - return -EOPNOTSUPP; - break; + return vnt_set_keys(hw, sta, vif, key); case DISABLE_KEY: if (test_bit(key->hw_key_idx, &priv->key_entry_inuse)) clear_bit(key->hw_key_idx, &priv->key_entry_inuse); From 8e2e79ff61f15f1af6f08aa754bacaf0d504265c Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 2 Feb 2020 19:48:43 +0000 Subject: [PATCH 0013/2231] staging: vt6656: Remove fall back functions and headers. Fall back is no longer used in driver so remove all functions and headers. Signed-off-by: Malcolm Priestley Link: https://lore.kernel.org/r/33732aad-5905-c173-ea81-431265e4f7e7@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vt6656/device.h | 1 - drivers/staging/vt6656/rxtx.c | 206 -------------------------------- drivers/staging/vt6656/rxtx.h | 60 ---------- 3 files changed, 267 deletions(-) diff --git a/drivers/staging/vt6656/device.h b/drivers/staging/vt6656/device.h index fe6c11266123..da868adba7c9 100644 --- a/drivers/staging/vt6656/device.h +++ b/drivers/staging/vt6656/device.h @@ -244,7 +244,6 @@ struct vnt_usb_send_context { u8 pkt_no; u8 pkt_type; u8 need_ack; - u8 fb_option; bool in_use; unsigned char data[MAX_TOTAL_SIZE_WITH_ALL_HEADERS]; }; diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c index 0fabeb396563..74c1062dbaaa 100644 --- a/drivers/staging/vt6656/rxtx.c +++ b/drivers/staging/vt6656/rxtx.c @@ -39,30 +39,12 @@ static const u16 vnt_time_stampoff[2][MAX_RATE] = { {384, 192, 130, 113, 54, 43, 37, 31, 28, 25, 24, 23}, }; -static const u16 vnt_fb_opt0[2][5] = { - {RATE_12M, RATE_18M, RATE_24M, RATE_36M, RATE_48M}, /* fallback_rate0 */ - {RATE_12M, RATE_12M, RATE_18M, RATE_24M, RATE_36M}, /* fallback_rate1 */ -}; - -static const u16 vnt_fb_opt1[2][5] = { - {RATE_12M, RATE_18M, RATE_24M, RATE_24M, RATE_36M}, /* fallback_rate0 */ - {RATE_6M, RATE_6M, RATE_12M, RATE_12M, RATE_18M}, /* fallback_rate1 */ -}; - #define RTSDUR_BB 0 #define RTSDUR_BA 1 #define RTSDUR_AA 2 #define CTSDUR_BA 3 -#define RTSDUR_BA_F0 4 -#define RTSDUR_AA_F0 5 -#define RTSDUR_BA_F1 6 -#define RTSDUR_AA_F1 7 -#define CTSDUR_BA_F0 8 -#define CTSDUR_BA_F1 9 #define DATADUR_B 10 #define DATADUR_A 11 -#define DATADUR_A_F0 12 -#define DATADUR_A_F1 13 static struct vnt_usb_send_context *vnt_get_free_context(struct vnt_private *priv) @@ -216,8 +198,6 @@ static __le16 vnt_get_rtscts_duration_le(struct vnt_usb_send_context *context, switch (dur_type) { case RTSDUR_BB: case RTSDUR_BA: - case RTSDUR_BA_F0: - case RTSDUR_BA_F1: cts_time = vnt_get_frame_time(priv->preamble_type, pkt_type, 14, priv->top_cck_basic_rate); dur_time = cts_time + 2 * priv->sifs + @@ -226,8 +206,6 @@ static __le16 vnt_get_rtscts_duration_le(struct vnt_usb_send_context *context, break; case RTSDUR_AA: - case RTSDUR_AA_F0: - case RTSDUR_AA_F1: cts_time = vnt_get_frame_time(priv->preamble_type, pkt_type, 14, priv->top_ofdm_basic_rate); dur_time = cts_time + 2 * priv->sifs + @@ -236,8 +214,6 @@ static __le16 vnt_get_rtscts_duration_le(struct vnt_usb_send_context *context, break; case CTSDUR_BA: - case CTSDUR_BA_F0: - case CTSDUR_BA_F1: dur_time = priv->sifs + vnt_get_rsvtime(priv, pkt_type, frame_length, rate, need_ack); break; @@ -297,63 +273,6 @@ static u16 vnt_rxtx_datahead_g(struct vnt_usb_send_context *tx_context, return le16_to_cpu(buf->duration_a); } -static u16 vnt_rxtx_datahead_g_fb(struct vnt_usb_send_context *tx_context, - struct vnt_tx_datahead_g_fb *buf) -{ - struct vnt_private *priv = tx_context->priv; - u32 frame_len = tx_context->frame_len; - u16 rate = tx_context->tx_rate; - u8 need_ack = tx_context->need_ack; - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, frame_len, rate, tx_context->pkt_type, &buf->a); - - vnt_get_phy_field(priv, frame_len, priv->top_cck_basic_rate, - PK_TYPE_11B, &buf->b); - - /* Get Duration and TimeStamp */ - buf->duration_a = vnt_get_duration_le(priv, tx_context->pkt_type, - need_ack); - buf->duration_b = vnt_get_duration_le(priv, PK_TYPE_11B, need_ack); - - buf->duration_a_f0 = vnt_get_duration_le(priv, tx_context->pkt_type, - need_ack); - buf->duration_a_f1 = vnt_get_duration_le(priv, tx_context->pkt_type, - need_ack); - - buf->time_stamp_off_a = vnt_time_stamp_off(priv, rate); - buf->time_stamp_off_b = vnt_time_stamp_off(priv, - priv->top_cck_basic_rate); - - tx_context->tx_hdr_size = vnt_mac_hdr_pos(tx_context, &buf->hdr); - - return le16_to_cpu(buf->duration_a); -} - -static u16 vnt_rxtx_datahead_a_fb(struct vnt_usb_send_context *tx_context, - struct vnt_tx_datahead_a_fb *buf) -{ - struct vnt_private *priv = tx_context->priv; - u16 rate = tx_context->tx_rate; - u8 pkt_type = tx_context->pkt_type; - u8 need_ack = tx_context->need_ack; - u32 frame_len = tx_context->frame_len; - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, frame_len, rate, pkt_type, &buf->a); - /* Get Duration and TimeStampOff */ - buf->duration = vnt_get_duration_le(priv, pkt_type, need_ack); - - buf->duration_f0 = vnt_get_duration_le(priv, pkt_type, need_ack); - buf->duration_f1 = vnt_get_duration_le(priv, pkt_type, need_ack); - - buf->time_stamp_off = vnt_time_stamp_off(priv, rate); - - tx_context->tx_hdr_size = vnt_mac_hdr_pos(tx_context, &buf->hdr); - - return le16_to_cpu(buf->duration); -} - static u16 vnt_rxtx_datahead_ab(struct vnt_usb_send_context *tx_context, struct vnt_tx_datahead_ab *buf) { @@ -426,50 +345,6 @@ static u16 vnt_rxtx_rts_g_head(struct vnt_usb_send_context *tx_context, return vnt_rxtx_datahead_g(tx_context, &buf->data_head); } -static u16 vnt_rxtx_rts_g_fb_head(struct vnt_usb_send_context *tx_context, - struct vnt_rts_g_fb *buf) -{ - struct vnt_private *priv = tx_context->priv; - u16 current_rate = tx_context->tx_rate; - u16 rts_frame_len = 20; - - vnt_get_phy_field(priv, rts_frame_len, priv->top_cck_basic_rate, - PK_TYPE_11B, &buf->b); - vnt_get_phy_field(priv, rts_frame_len, priv->top_ofdm_basic_rate, - tx_context->pkt_type, &buf->a); - - buf->duration_bb = vnt_get_rtscts_duration_le(tx_context, RTSDUR_BB, - PK_TYPE_11B, - priv->top_cck_basic_rate); - buf->duration_aa = vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA, - tx_context->pkt_type, - current_rate); - buf->duration_ba = vnt_get_rtscts_duration_le(tx_context, RTSDUR_BA, - tx_context->pkt_type, - current_rate); - - buf->rts_duration_ba_f0 = - vnt_get_rtscts_duration_le(tx_context, RTSDUR_BA_F0, - tx_context->pkt_type, - priv->tx_rate_fb0); - buf->rts_duration_aa_f0 = - vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA_F0, - tx_context->pkt_type, - priv->tx_rate_fb0); - buf->rts_duration_ba_f1 = - vnt_get_rtscts_duration_le(tx_context, RTSDUR_BA_F1, - tx_context->pkt_type, - priv->tx_rate_fb1); - buf->rts_duration_aa_f1 = - vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA_F1, - tx_context->pkt_type, - priv->tx_rate_fb1); - - vnt_fill_ieee80211_rts(tx_context, &buf->data, buf->duration_aa); - - return vnt_rxtx_datahead_g_fb(tx_context, &buf->data_head); -} - static u16 vnt_rxtx_rts_ab_head(struct vnt_usb_send_context *tx_context, struct vnt_rts_ab *buf) { @@ -489,71 +364,6 @@ static u16 vnt_rxtx_rts_ab_head(struct vnt_usb_send_context *tx_context, return vnt_rxtx_datahead_ab(tx_context, &buf->data_head); } -static u16 vnt_rxtx_rts_a_fb_head(struct vnt_usb_send_context *tx_context, - struct vnt_rts_a_fb *buf) -{ - struct vnt_private *priv = tx_context->priv; - u16 current_rate = tx_context->tx_rate; - u16 rts_frame_len = 20; - - vnt_get_phy_field(priv, rts_frame_len, priv->top_ofdm_basic_rate, - tx_context->pkt_type, &buf->a); - - buf->duration = vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA, - tx_context->pkt_type, - current_rate); - - buf->rts_duration_f0 = - vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA_F0, - tx_context->pkt_type, - priv->tx_rate_fb0); - - buf->rts_duration_f1 = - vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA_F1, - tx_context->pkt_type, - priv->tx_rate_fb1); - - vnt_fill_ieee80211_rts(tx_context, &buf->data, buf->duration); - - return vnt_rxtx_datahead_a_fb(tx_context, &buf->data_head); -} - -static u16 vnt_fill_cts_fb_head(struct vnt_usb_send_context *tx_context, - union vnt_tx_data_head *head) -{ - struct vnt_private *priv = tx_context->priv; - struct vnt_cts_fb *buf = &head->cts_g_fb; - u32 cts_frame_len = 14; - u16 current_rate = tx_context->tx_rate; - - /* Get SignalField,ServiceField,Length */ - vnt_get_phy_field(priv, cts_frame_len, priv->top_cck_basic_rate, - PK_TYPE_11B, &buf->b); - - buf->duration_ba = - vnt_get_rtscts_duration_le(tx_context, CTSDUR_BA, - tx_context->pkt_type, - current_rate); - /* Get CTSDuration_ba_f0 */ - buf->cts_duration_ba_f0 = - vnt_get_rtscts_duration_le(tx_context, CTSDUR_BA_F0, - tx_context->pkt_type, - priv->tx_rate_fb0); - /* Get CTSDuration_ba_f1 */ - buf->cts_duration_ba_f1 = - vnt_get_rtscts_duration_le(tx_context, CTSDUR_BA_F1, - tx_context->pkt_type, - priv->tx_rate_fb1); - /* Get CTS Frame body */ - buf->data.duration = buf->duration_ba; - buf->data.frame_control = - cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); - - ether_addr_copy(buf->data.ra, priv->current_net_addr); - - return vnt_rxtx_datahead_g_fb(tx_context, &buf->data_head); -} - static u16 vnt_fill_cts_head(struct vnt_usb_send_context *tx_context, union vnt_tx_data_head *head) { @@ -606,9 +416,6 @@ static u16 vnt_rxtx_rts(struct vnt_usb_send_context *tx_context, if (need_mic) head = &tx_head->tx_rts.tx.mic.head; - if (tx_context->fb_option) - return vnt_rxtx_rts_g_fb_head(tx_context, &head->rts_g_fb); - return vnt_rxtx_rts_g_head(tx_context, &head->rts_g); } @@ -633,10 +440,6 @@ static u16 vnt_rxtx_cts(struct vnt_usb_send_context *tx_context, if (need_mic) head = &tx_head->tx_cts.tx.mic.head; - /* Fill CTS */ - if (tx_context->fb_option) - return vnt_fill_cts_fb_head(tx_context, head); - return vnt_fill_cts_head(tx_context, head); } @@ -664,18 +467,9 @@ static u16 vnt_rxtx_ab(struct vnt_usb_send_context *tx_context, buf->rts_rrv_time = vnt_get_rtscts_rsvtime_le(priv, 2, tx_context->pkt_type, frame_len, current_rate); - if (tx_context->fb_option && - tx_context->pkt_type == PK_TYPE_11A) - return vnt_rxtx_rts_a_fb_head(tx_context, - &head->rts_a_fb); - return vnt_rxtx_rts_ab_head(tx_context, &head->rts_ab); } - if (tx_context->pkt_type == PK_TYPE_11A) - return vnt_rxtx_datahead_a_fb(tx_context, - &head->data_head_a_fb); - return vnt_rxtx_datahead_ab(tx_context, &head->data_head_ab); } diff --git a/drivers/staging/vt6656/rxtx.h b/drivers/staging/vt6656/rxtx.h index d528607e02bf..10076efa4522 100644 --- a/drivers/staging/vt6656/rxtx.h +++ b/drivers/staging/vt6656/rxtx.h @@ -73,18 +73,6 @@ struct vnt_tx_datahead_g { struct ieee80211_hdr hdr; } __packed; -struct vnt_tx_datahead_g_fb { - struct vnt_phy_field b; - struct vnt_phy_field a; - __le16 duration_b; - __le16 duration_a; - __le16 duration_a_f0; - __le16 duration_a_f1; - __le16 time_stamp_off_b; - __le16 time_stamp_off_a; - struct ieee80211_hdr hdr; -} __packed; - struct vnt_tx_datahead_ab { struct vnt_phy_field ab; __le16 duration; @@ -92,15 +80,6 @@ struct vnt_tx_datahead_ab { struct ieee80211_hdr hdr; } __packed; -struct vnt_tx_datahead_a_fb { - struct vnt_phy_field a; - __le16 duration; - __le16 time_stamp_off; - __le16 duration_f0; - __le16 duration_f1; - struct ieee80211_hdr hdr; -} __packed; - /* RTS buffer header */ struct vnt_rts_g { struct vnt_phy_field b; @@ -113,21 +92,6 @@ struct vnt_rts_g { struct vnt_tx_datahead_g data_head; } __packed; -struct vnt_rts_g_fb { - struct vnt_phy_field b; - struct vnt_phy_field a; - __le16 duration_ba; - __le16 duration_aa; - __le16 duration_bb; - u16 wReserved; - __le16 rts_duration_ba_f0; - __le16 rts_duration_aa_f0; - __le16 rts_duration_ba_f1; - __le16 rts_duration_aa_f1; - struct ieee80211_rts data; - struct vnt_tx_datahead_g_fb data_head; -} __packed; - struct vnt_rts_ab { struct vnt_phy_field ab; __le16 duration; @@ -136,15 +100,6 @@ struct vnt_rts_ab { struct vnt_tx_datahead_ab data_head; } __packed; -struct vnt_rts_a_fb { - struct vnt_phy_field a; - __le16 duration; - u16 wReserved; - __le16 rts_duration_f0; - __le16 rts_duration_f1; - struct ieee80211_rts data; - struct vnt_tx_datahead_a_fb data_head; -} __packed; /* CTS buffer header */ struct vnt_cts { @@ -156,29 +111,14 @@ struct vnt_cts { struct vnt_tx_datahead_g data_head; } __packed; -struct vnt_cts_fb { - struct vnt_phy_field b; - __le16 duration_ba; - u16 wReserved; - __le16 cts_duration_ba_f0; - __le16 cts_duration_ba_f1; - struct ieee80211_cts data; - u16 reserved2; - struct vnt_tx_datahead_g_fb data_head; -} __packed; - union vnt_tx_data_head { /* rts g */ struct vnt_rts_g rts_g; - struct vnt_rts_g_fb rts_g_fb; /* rts a/b */ struct vnt_rts_ab rts_ab; - struct vnt_rts_a_fb rts_a_fb; /* cts g */ struct vnt_cts cts_g; - struct vnt_cts_fb cts_g_fb; /* no rts/cts */ - struct vnt_tx_datahead_a_fb data_head_a_fb; struct vnt_tx_datahead_ab data_head_ab; }; From 034280e33ea0bb00964adc30dddaf9a48ce75a8c Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Mon, 3 Feb 2020 10:39:43 +0000 Subject: [PATCH 0014/2231] staging: wilc1000: refactor SPI read/write commands handling API's Refactor SPI commands handling by making use of 'struct' for data exchange and extraction of information flow between host and firmware. The SPI read/write commands are now handled in separate function instead of using a single function to process all types of command. The use of 'struct' helped to make the code self explanatory. These points were discussed and suggested during code review [1]. 1. https://www.spinics.net/lists/linux-wireless/msg191489.html Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200203160848.4052-1-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/spi.c | 816 ++++++++++++++++----------------- 1 file changed, 405 insertions(+), 411 deletions(-) diff --git a/drivers/staging/wilc1000/spi.c b/drivers/staging/wilc1000/spi.c index 3ffc7b4fddf6..9a9f3624e158 100644 --- a/drivers/staging/wilc1000/spi.c +++ b/drivers/staging/wilc1000/spi.c @@ -70,6 +70,11 @@ static u8 crc7(u8 crc, const u8 *buffer, u32 len) return crc; } +static u8 wilc_get_crc7(u8 *buffer, u32 len) +{ + return crc7(0x7f, (const u8 *)buffer, len) << 1; +} + /******************************************** * * Spi protocol Function @@ -97,6 +102,52 @@ static u8 crc7(u8 crc, const u8 *buffer, u32 len) #define USE_SPI_DMA 0 +#define WILC_SPI_COMMAND_STAT_SUCCESS 0 +#define WILC_GET_RESP_HDR_START(h) (((h) >> 4) & 0xf) + +struct wilc_spi_cmd { + u8 cmd_type; + union { + struct { + u8 addr[3]; + u8 crc[0]; + } __packed simple_cmd; + struct { + u8 addr[3]; + u8 size[2]; + u8 crc[0]; + } __packed dma_cmd; + struct { + u8 addr[3]; + u8 size[3]; + u8 crc[0]; + } __packed dma_cmd_ext; + struct { + u8 addr[2]; + __be32 data; + u8 crc[0]; + } __packed internal_w_cmd; + struct { + u8 addr[3]; + __be32 data; + u8 crc[0]; + } __packed w_cmd; + } u; +} __packed; + +struct wilc_spi_read_rsp_data { + u8 rsp_cmd_type; + u8 status; + u8 resp_header; + u8 resp_data[4]; + u8 crc[0]; +} __packed; + +struct wilc_spi_rsp_data { + u8 rsp_cmd_type; + u8 status; +} __packed; + static int wilc_bus_probe(struct spi_device *spi) { int ret; @@ -284,335 +335,6 @@ static int wilc_spi_tx_rx(struct wilc *wilc, u8 *wb, u8 *rb, u32 rlen) return ret; } -static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz, - u8 clockless) -{ - struct spi_device *spi = to_spi_device(wilc->dev); - struct wilc_spi *spi_priv = wilc->bus_data; - u8 wb[32], rb[32]; - u8 wix, rix; - u32 len2; - u8 rsp; - int len = 0; - int result = 0; - int retry; - u8 crc[2]; - - wb[0] = cmd; - switch (cmd) { - case CMD_SINGLE_READ: /* single word (4 bytes) read */ - wb[1] = (u8)(adr >> 16); - wb[2] = (u8)(adr >> 8); - wb[3] = (u8)adr; - len = 5; - break; - - case CMD_INTERNAL_READ: /* internal register read */ - wb[1] = (u8)(adr >> 8); - if (clockless == 1) - wb[1] |= BIT(7); - wb[2] = (u8)adr; - wb[3] = 0x00; - len = 5; - break; - - case CMD_TERMINATE: - wb[1] = 0x00; - wb[2] = 0x00; - wb[3] = 0x00; - len = 5; - break; - - case CMD_REPEAT: - wb[1] = 0x00; - wb[2] = 0x00; - wb[3] = 0x00; - len = 5; - break; - - case CMD_RESET: - wb[1] = 0xff; - wb[2] = 0xff; - wb[3] = 0xff; - len = 5; - break; - - case CMD_DMA_WRITE: /* dma write */ - case CMD_DMA_READ: /* dma read */ - wb[1] = (u8)(adr >> 16); - wb[2] = (u8)(adr >> 8); - wb[3] = (u8)adr; - wb[4] = (u8)(sz >> 8); - wb[5] = (u8)(sz); - len = 7; - break; - - case CMD_DMA_EXT_WRITE: /* dma extended write */ - case CMD_DMA_EXT_READ: /* dma extended read */ - wb[1] = (u8)(adr >> 16); - wb[2] = (u8)(adr >> 8); - wb[3] = (u8)adr; - wb[4] = (u8)(sz >> 16); - wb[5] = (u8)(sz >> 8); - wb[6] = (u8)(sz); - len = 8; - break; - - case CMD_INTERNAL_WRITE: /* internal register write */ - wb[1] = (u8)(adr >> 8); - if (clockless == 1) - wb[1] |= BIT(7); - wb[2] = (u8)(adr); - wb[3] = b[3]; - wb[4] = b[2]; - wb[5] = b[1]; - wb[6] = b[0]; - len = 8; - break; - - case CMD_SINGLE_WRITE: /* single word write */ - wb[1] = (u8)(adr >> 16); - wb[2] = (u8)(adr >> 8); - wb[3] = (u8)(adr); - wb[4] = b[3]; - wb[5] = b[2]; - wb[6] = b[1]; - wb[7] = b[0]; - len = 9; - break; - - default: - result = -EINVAL; - break; - } - - if (result) - return result; - - if (!spi_priv->crc_off) - wb[len - 1] = (crc7(0x7f, (const u8 *)&wb[0], len - 1)) << 1; - else - len -= 1; - -#define NUM_SKIP_BYTES (1) -#define NUM_RSP_BYTES (2) -#define NUM_DATA_HDR_BYTES (1) -#define NUM_DATA_BYTES (4) -#define NUM_CRC_BYTES (2) -#define NUM_DUMMY_BYTES (3) - if (cmd == CMD_RESET || - cmd == CMD_TERMINATE || - cmd == CMD_REPEAT) { - len2 = len + (NUM_SKIP_BYTES + NUM_RSP_BYTES + NUM_DUMMY_BYTES); - } else if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) { - int tmp = NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES - + NUM_DUMMY_BYTES; - if (!spi_priv->crc_off) - len2 = len + tmp + NUM_CRC_BYTES; - else - len2 = len + tmp; - } else { - len2 = len + (NUM_RSP_BYTES + NUM_DUMMY_BYTES); - } -#undef NUM_DUMMY_BYTES - - if (len2 > ARRAY_SIZE(wb)) { - dev_err(&spi->dev, "spi buffer size too small (%d) (%zu)\n", - len2, ARRAY_SIZE(wb)); - return -EINVAL; - } - /* zero spi write buffers. */ - for (wix = len; wix < len2; wix++) - wb[wix] = 0; - rix = len; - - if (wilc_spi_tx_rx(wilc, wb, rb, len2)) { - dev_err(&spi->dev, "Failed cmd write, bus error...\n"); - return -EINVAL; - } - - /* - * Command/Control response - */ - if (cmd == CMD_RESET || cmd == CMD_TERMINATE || cmd == CMD_REPEAT) - rix++; /* skip 1 byte */ - - rsp = rb[rix++]; - - if (rsp != cmd) { - dev_err(&spi->dev, - "Failed cmd response, cmd (%02x), resp (%02x)\n", - cmd, rsp); - return -EINVAL; - } - - /* - * State response - */ - rsp = rb[rix++]; - if (rsp != 0x00) { - dev_err(&spi->dev, "Failed cmd state response state (%02x)\n", - rsp); - return -EINVAL; - } - - if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ || - cmd == CMD_DMA_READ || cmd == CMD_DMA_EXT_READ) { - /* - * Data Respnose header - */ - retry = 100; - do { - /* - * ensure there is room in buffer later - * to read data and crc - */ - if (rix < len2) { - rsp = rb[rix++]; - } else { - retry = 0; - break; - } - if (((rsp >> 4) & 0xf) == 0xf) - break; - } while (retry--); - - if (retry <= 0) { - dev_err(&spi->dev, - "Error, data read response (%02x)\n", rsp); - return -EAGAIN; - } - } - - if (cmd == CMD_INTERNAL_READ || cmd == CMD_SINGLE_READ) { - /* - * Read bytes - */ - if ((rix + 3) < len2) { - b[0] = rb[rix++]; - b[1] = rb[rix++]; - b[2] = rb[rix++]; - b[3] = rb[rix++]; - } else { - dev_err(&spi->dev, - "buffer overrun when reading data.\n"); - return -EINVAL; - } - - if (!spi_priv->crc_off) { - /* - * Read Crc - */ - if ((rix + 1) < len2) { - crc[0] = rb[rix++]; - crc[1] = rb[rix++]; - } else { - dev_err(&spi->dev, - "buffer overrun when reading crc.\n"); - return -EINVAL; - } - } - } else if ((cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) { - int ix; - - /* some data may be read in response to dummy bytes. */ - for (ix = 0; (rix < len2) && (ix < sz); ) - b[ix++] = rb[rix++]; - - sz -= ix; - - if (sz > 0) { - int nbytes; - - if (sz <= (DATA_PKT_SZ - ix)) - nbytes = sz; - else - nbytes = DATA_PKT_SZ - ix; - - /* - * Read bytes - */ - if (wilc_spi_rx(wilc, &b[ix], nbytes)) { - dev_err(&spi->dev, - "Failed block read, bus err\n"); - return -EINVAL; - } - - /* - * Read Crc - */ - if (!spi_priv->crc_off && wilc_spi_rx(wilc, crc, 2)) { - dev_err(&spi->dev, - "Failed block crc read, bus err\n"); - return -EINVAL; - } - - ix += nbytes; - sz -= nbytes; - } - - /* - * if any data in left unread, - * then read the rest using normal DMA code. - */ - while (sz > 0) { - int nbytes; - - if (sz <= DATA_PKT_SZ) - nbytes = sz; - else - nbytes = DATA_PKT_SZ; - - /* - * read data response only on the next DMA cycles not - * the first DMA since data response header is already - * handled above for the first DMA. - */ - /* - * Data Respnose header - */ - retry = 10; - do { - if (wilc_spi_rx(wilc, &rsp, 1)) { - dev_err(&spi->dev, - "Failed resp read, bus err\n"); - result = -EINVAL; - break; - } - if (((rsp >> 4) & 0xf) == 0xf) - break; - } while (retry--); - - if (result) - break; - - /* - * Read bytes - */ - if (wilc_spi_rx(wilc, &b[ix], nbytes)) { - dev_err(&spi->dev, - "Failed block read, bus err\n"); - result = -EINVAL; - break; - } - - /* - * Read Crc - */ - if (!spi_priv->crc_off && wilc_spi_rx(wilc, crc, 2)) { - dev_err(&spi->dev, - "Failed block crc read, bus err\n"); - result = -EINVAL; - break; - } - - ix += nbytes; - sz -= nbytes; - } - } - return result; -} - static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz) { struct spi_device *spi = to_spi_device(wilc->dev); @@ -686,91 +408,273 @@ static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz) * Spi Internal Read/Write Function * ********************************************/ - -static int spi_internal_write(struct wilc *wilc, u32 adr, u32 dat) +static int wilc_spi_single_read(struct wilc *wilc, u8 cmd, u32 adr, void *b, + u8 clockless) { struct spi_device *spi = to_spi_device(wilc->dev); - int result; + struct wilc_spi *spi_priv = wilc->bus_data; + u8 wb[32], rb[32]; + int cmd_len, resp_len; + u8 crc[2]; + struct wilc_spi_cmd *c; + struct wilc_spi_read_rsp_data *r; - cpu_to_le32s(&dat); - result = spi_cmd_complete(wilc, CMD_INTERNAL_WRITE, adr, (u8 *)&dat, 4, - 0); - if (result) - dev_err(&spi->dev, "Failed internal write cmd...\n"); - - return result; -} - -static int spi_internal_read(struct wilc *wilc, u32 adr, u32 *data) -{ - struct spi_device *spi = to_spi_device(wilc->dev); - int result; - - result = spi_cmd_complete(wilc, CMD_INTERNAL_READ, adr, (u8 *)data, 4, - 0); - if (result) { - dev_err(&spi->dev, "Failed internal read cmd...\n"); - return result; - } - - le32_to_cpus(data); - - return result; -} - -/******************************************** - * - * Spi interfaces - * - ********************************************/ - -static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data) -{ - struct spi_device *spi = to_spi_device(wilc->dev); - int result; - u8 cmd = CMD_SINGLE_WRITE; - u8 clockless = 0; - - cpu_to_le32s(&data); - if (addr < 0x30) { - /* Clockless register */ - cmd = CMD_INTERNAL_WRITE; - clockless = 1; - } - - result = spi_cmd_complete(wilc, cmd, addr, (u8 *)&data, 4, clockless); - if (result) - dev_err(&spi->dev, "Failed cmd, write reg (%08x)...\n", addr); - - return result; -} - -static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size) -{ - struct spi_device *spi = to_spi_device(wilc->dev); - int result; - - /* - * has to be greated than 4 - */ - if (size <= 4) + memset(wb, 0x0, sizeof(wb)); + memset(rb, 0x0, sizeof(rb)); + c = (struct wilc_spi_cmd *)wb; + c->cmd_type = cmd; + if (cmd == CMD_SINGLE_READ) { + c->u.simple_cmd.addr[0] = adr >> 16; + c->u.simple_cmd.addr[1] = adr >> 8; + c->u.simple_cmd.addr[2] = adr; + } else if (cmd == CMD_INTERNAL_READ) { + c->u.simple_cmd.addr[0] = adr >> 8; + if (clockless == 1) + c->u.simple_cmd.addr[0] |= BIT(7); + c->u.simple_cmd.addr[1] = adr; + c->u.simple_cmd.addr[2] = 0x0; + } else { + dev_err(&spi->dev, "cmd [%x] not supported\n", cmd); return -EINVAL; - - result = spi_cmd_complete(wilc, CMD_DMA_EXT_WRITE, addr, NULL, size, 0); - if (result) { - dev_err(&spi->dev, - "Failed cmd, write block (%08x)...\n", addr); - return result; } - /* - * Data - */ - result = spi_data_write(wilc, buf, size); - if (result) - dev_err(&spi->dev, "Failed block data write...\n"); + cmd_len = offsetof(struct wilc_spi_cmd, u.simple_cmd.crc); + resp_len = sizeof(*r); + if (!spi_priv->crc_off) { + c->u.simple_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); + cmd_len += 1; + resp_len += 2; + } - return result; + if (cmd_len + resp_len > ARRAY_SIZE(wb)) { + dev_err(&spi->dev, + "spi buffer size too small (%d) (%d) (%zu)\n", + cmd_len, resp_len, ARRAY_SIZE(wb)); + return -EINVAL; + } + + if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) { + dev_err(&spi->dev, "Failed cmd write, bus error...\n"); + return -EINVAL; + } + + r = (struct wilc_spi_read_rsp_data *)&rb[cmd_len]; + if (r->rsp_cmd_type != cmd) { + dev_err(&spi->dev, + "Failed cmd response, cmd (%02x), resp (%02x)\n", + cmd, r->rsp_cmd_type); + return -EINVAL; + } + + if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) { + dev_err(&spi->dev, "Failed cmd state response state (%02x)\n", + r->status); + return -EINVAL; + } + + if (WILC_GET_RESP_HDR_START(r->resp_header) != 0xf) { + dev_err(&spi->dev, "Error, data read response (%02x)\n", + r->resp_header); + return -EINVAL; + } + + if (b) + memcpy(b, r->resp_data, 4); + + if (!spi_priv->crc_off) + memcpy(crc, r->crc, 2); + + return 0; +} + +static int wilc_spi_write_cmd(struct wilc *wilc, u8 cmd, u32 adr, u32 data, + u8 clockless) +{ + struct spi_device *spi = to_spi_device(wilc->dev); + struct wilc_spi *spi_priv = wilc->bus_data; + u8 wb[32], rb[32]; + int cmd_len, resp_len; + struct wilc_spi_cmd *c; + struct wilc_spi_rsp_data *r; + + memset(wb, 0x0, sizeof(wb)); + memset(rb, 0x0, sizeof(rb)); + c = (struct wilc_spi_cmd *)wb; + c->cmd_type = cmd; + if (cmd == CMD_INTERNAL_WRITE) { + c->u.internal_w_cmd.addr[0] = adr >> 8; + if (clockless == 1) + c->u.internal_w_cmd.addr[0] |= BIT(7); + + c->u.internal_w_cmd.addr[1] = adr; + c->u.internal_w_cmd.data = cpu_to_be32(data); + cmd_len = offsetof(struct wilc_spi_cmd, u.internal_w_cmd.crc); + if (!spi_priv->crc_off) + c->u.internal_w_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); + } else if (cmd == CMD_SINGLE_WRITE) { + c->u.w_cmd.addr[0] = adr >> 16; + c->u.w_cmd.addr[1] = adr >> 8; + c->u.w_cmd.addr[2] = adr; + c->u.w_cmd.data = cpu_to_be32(data); + cmd_len = offsetof(struct wilc_spi_cmd, u.w_cmd.crc); + if (!spi_priv->crc_off) + c->u.w_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); + } else { + dev_err(&spi->dev, "write cmd [%x] not supported\n", cmd); + return -EINVAL; + } + + if (!spi_priv->crc_off) + cmd_len += 1; + + resp_len = sizeof(*r); + + if (cmd_len + resp_len > ARRAY_SIZE(wb)) { + dev_err(&spi->dev, + "spi buffer size too small (%d) (%d) (%zu)\n", + cmd_len, resp_len, ARRAY_SIZE(wb)); + return -EINVAL; + } + + if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) { + dev_err(&spi->dev, "Failed cmd write, bus error...\n"); + return -EINVAL; + } + + r = (struct wilc_spi_rsp_data *)&rb[cmd_len]; + if (r->rsp_cmd_type != cmd) { + dev_err(&spi->dev, + "Failed cmd response, cmd (%02x), resp (%02x)\n", + cmd, r->rsp_cmd_type); + return -EINVAL; + } + + if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) { + dev_err(&spi->dev, "Failed cmd state response state (%02x)\n", + r->status); + return -EINVAL; + } + + return 0; +} + +static int wilc_spi_dma_rw(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz) +{ + struct spi_device *spi = to_spi_device(wilc->dev); + struct wilc_spi *spi_priv = wilc->bus_data; + u8 wb[32], rb[32]; + int cmd_len, resp_len; + int retry, ix = 0; + u8 crc[2]; + struct wilc_spi_cmd *c; + struct wilc_spi_rsp_data *r; + + memset(wb, 0x0, sizeof(wb)); + memset(rb, 0x0, sizeof(rb)); + c = (struct wilc_spi_cmd *)wb; + c->cmd_type = cmd; + if (cmd == CMD_DMA_WRITE || cmd == CMD_DMA_READ) { + c->u.dma_cmd.addr[0] = adr >> 16; + c->u.dma_cmd.addr[1] = adr >> 8; + c->u.dma_cmd.addr[2] = adr; + c->u.dma_cmd.size[0] = sz >> 8; + c->u.dma_cmd.size[1] = sz; + cmd_len = offsetof(struct wilc_spi_cmd, u.dma_cmd.crc); + if (!spi_priv->crc_off) + c->u.dma_cmd.crc[0] = wilc_get_crc7(wb, cmd_len); + } else if (cmd == CMD_DMA_EXT_WRITE || cmd == CMD_DMA_EXT_READ) { + c->u.dma_cmd_ext.addr[0] = adr >> 16; + c->u.dma_cmd_ext.addr[1] = adr >> 8; + c->u.dma_cmd_ext.addr[2] = adr; + c->u.dma_cmd_ext.size[0] = sz >> 16; + c->u.dma_cmd_ext.size[1] = sz >> 8; + c->u.dma_cmd_ext.size[2] = sz; + cmd_len = offsetof(struct wilc_spi_cmd, u.dma_cmd_ext.crc); + if (!spi_priv->crc_off) + c->u.dma_cmd_ext.crc[0] = wilc_get_crc7(wb, cmd_len); + } else { + dev_err(&spi->dev, "dma read write cmd [%x] not supported\n", + cmd); + return -EINVAL; + } + if (!spi_priv->crc_off) + cmd_len += 1; + + resp_len = sizeof(*r); + + if (cmd_len + resp_len > ARRAY_SIZE(wb)) { + dev_err(&spi->dev, "spi buffer size too small (%d)(%d) (%zu)\n", + cmd_len, resp_len, ARRAY_SIZE(wb)); + return -EINVAL; + } + + if (wilc_spi_tx_rx(wilc, wb, rb, cmd_len + resp_len)) { + dev_err(&spi->dev, "Failed cmd write, bus error...\n"); + return -EINVAL; + } + + r = (struct wilc_spi_rsp_data *)&rb[cmd_len]; + if (r->rsp_cmd_type != cmd) { + dev_err(&spi->dev, + "Failed cmd response, cmd (%02x), resp (%02x)\n", + cmd, r->rsp_cmd_type); + return -EINVAL; + } + + if (r->status != WILC_SPI_COMMAND_STAT_SUCCESS) { + dev_err(&spi->dev, "Failed cmd state response state (%02x)\n", + r->status); + return -EINVAL; + } + + if (cmd == CMD_DMA_WRITE || cmd == CMD_DMA_EXT_WRITE) + return 0; + + while (sz > 0) { + int nbytes; + u8 rsp; + + if (sz <= DATA_PKT_SZ) + nbytes = sz; + else + nbytes = DATA_PKT_SZ; + + /* + * Data Response header + */ + retry = 100; + do { + if (wilc_spi_rx(wilc, &rsp, 1)) { + dev_err(&spi->dev, + "Failed resp read, bus err\n"); + return -EINVAL; + } + if (WILC_GET_RESP_HDR_START(rsp) == 0xf) + break; + } while (retry--); + + /* + * Read bytes + */ + if (wilc_spi_rx(wilc, &b[ix], nbytes)) { + dev_err(&spi->dev, + "Failed block read, bus err\n"); + return -EINVAL; + } + + /* + * Read Crc + */ + if (!spi_priv->crc_off && wilc_spi_rx(wilc, crc, 2)) { + dev_err(&spi->dev, + "Failed block crc read, bus err\n"); + return -EINVAL; + } + + ix += nbytes; + sz -= nbytes; + } + return 0; } static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data) @@ -786,7 +690,7 @@ static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data) clockless = 1; } - result = spi_cmd_complete(wilc, cmd, addr, (u8 *)data, 4, clockless); + result = wilc_spi_single_read(wilc, cmd, addr, data, clockless); if (result) { dev_err(&spi->dev, "Failed cmd, read reg (%08x)...\n", addr); return result; @@ -805,11 +709,101 @@ static int wilc_spi_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size) if (size <= 4) return -EINVAL; - result = spi_cmd_complete(wilc, CMD_DMA_EXT_READ, addr, buf, size, 0); - if (result) + result = wilc_spi_dma_rw(wilc, CMD_DMA_EXT_READ, addr, buf, size); + if (result) { dev_err(&spi->dev, "Failed cmd, read block (%08x)...\n", addr); + return result; + } - return result; + return 0; +} + +static int spi_internal_write(struct wilc *wilc, u32 adr, u32 dat) +{ + struct spi_device *spi = to_spi_device(wilc->dev); + int result; + + result = wilc_spi_write_cmd(wilc, CMD_INTERNAL_WRITE, adr, dat, 0); + if (result) { + dev_err(&spi->dev, "Failed internal write cmd...\n"); + return result; + } + + return 0; +} + +static int spi_internal_read(struct wilc *wilc, u32 adr, u32 *data) +{ + struct spi_device *spi = to_spi_device(wilc->dev); + int result; + + result = wilc_spi_single_read(wilc, CMD_INTERNAL_READ, adr, data, 0); + if (result) { + dev_err(&spi->dev, "Failed internal read cmd...\n"); + return result; + } + + le32_to_cpus(data); + + return 0; +} + +/******************************************** + * + * Spi interfaces + * + ********************************************/ + +static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data) +{ + struct spi_device *spi = to_spi_device(wilc->dev); + int result; + u8 cmd = CMD_SINGLE_WRITE; + u8 clockless = 0; + + if (addr < 0x30) { + /* Clockless register */ + cmd = CMD_INTERNAL_WRITE; + clockless = 1; + } + + result = wilc_spi_write_cmd(wilc, cmd, addr, data, clockless); + if (result) { + dev_err(&spi->dev, "Failed cmd, write reg (%08x)...\n", addr); + return result; + } + + return 0; +} + +static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size) +{ + struct spi_device *spi = to_spi_device(wilc->dev); + int result; + + /* + * has to be greated than 4 + */ + if (size <= 4) + return -EINVAL; + + result = wilc_spi_dma_rw(wilc, CMD_DMA_EXT_WRITE, addr, NULL, size); + if (result) { + dev_err(&spi->dev, + "Failed cmd, write block (%08x)...\n", addr); + return result; + } + + /* + * Data + */ + result = spi_data_write(wilc, buf, size); + if (result) { + dev_err(&spi->dev, "Failed block data write...\n"); + return result; + } + + return 0; } /******************************************** From 7ed674e26357f8722120f3ca03077af45dfae6fc Mon Sep 17 00:00:00 2001 From: Tetsuhiro Kohada Date: Thu, 30 Jan 2020 16:06:13 +0900 Subject: [PATCH 0015/2231] staging: exfat: remove 'vol_type' variable. remove 'vol_type' variable. The following issues are described in exfat's TODO. > clean up the remaining vol_type checks, which are of two types: > some are ?: operators with magic numbers, and the rest are places > where we're doing stuff with '.' and '..'. The vol_type variable is always set to 'EXFAT'. The variable checks are unnessesary, so remove unused code. Signed-off-by: Tetsuhiro Kohada Reviewed-by: Mori Takahiro Suggested-by: Dan Carpenter Reviewed-by: Dan Carpenter Link: https://lore.kernel.org/r/20200130070614.11999-1-Kohada.Tetsuhiro@dc.MitsubishiElectric.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 1 - drivers/staging/exfat/exfat_core.c | 26 +++---------- drivers/staging/exfat/exfat_super.c | 58 ++++++++++------------------- 3 files changed, 26 insertions(+), 59 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 4d87360fab35..28d245b10e82 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -518,7 +518,6 @@ struct buf_cache_t { struct fs_info_t { u32 drv; /* drive ID */ - u32 vol_type; /* volume FAT type */ u32 vol_id; /* volume serial number */ u64 num_sectors; /* num of sectors in volume */ diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c index 07b460d01334..5a686289a1db 100644 --- a/drivers/staging/exfat/exfat_core.c +++ b/drivers/staging/exfat/exfat_core.c @@ -1560,11 +1560,7 @@ static s32 search_deleted_or_unused_entry(struct super_block *sb, if (num_empty >= num_entries) { p_fs->hint_uentry.dir = CLUSTER_32(~0); p_fs->hint_uentry.entry = -1; - - if (p_fs->vol_type == EXFAT) - return dentry - (num_entries - 1); - else - return dentry; + return dentry - (num_entries - 1); } } @@ -1914,7 +1910,7 @@ s32 count_dos_name_entries(struct super_block *sb, struct chain_t *p_dir, bool is_dir_empty(struct super_block *sb, struct chain_t *p_dir) { - int i, count = 0; + int i; s32 dentries_per_clu; u32 type; struct chain_t clu; @@ -1943,15 +1939,7 @@ bool is_dir_empty(struct super_block *sb, struct chain_t *p_dir) if (type == TYPE_UNUSED) return true; - if ((type != TYPE_FILE) && (type != TYPE_DIR)) - continue; - - if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ - return false; - - if (p_fs->vol_type == EXFAT) - return false; - if ((p_dir->dir == p_fs->root_dir) || ((++count) > 2)) + if ((type == TYPE_FILE) || (type == TYPE_DIR)) return false; } @@ -2128,7 +2116,6 @@ s32 exfat_mount(struct super_block *sb, struct pbr_sector_t *p_pbr) p_fs->num_clusters = GET32(p_bpb->clu_count) + 2; /* because the cluster index starts with 2 */ - p_fs->vol_type = EXFAT; p_fs->vol_id = GET32(p_bpb->vol_serial); p_fs->root_dir = GET32(p_bpb->root_cluster); @@ -2165,7 +2152,7 @@ s32 create_dir(struct inode *inode, struct chain_t *p_dir, clu.dir = CLUSTER_32(~0); clu.size = 0; - clu.flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + clu.flags = 0x03; /* (1) allocate a cluster */ ret = exfat_alloc_cluster(sb, 1, &clu); @@ -2198,7 +2185,7 @@ s32 create_dir(struct inode *inode, struct chain_t *p_dir, fid->entry = dentry; fid->attr = ATTR_SUBDIR; - fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->flags = 0x03; fid->size = size; fid->start_clu = clu.dir; @@ -2215,7 +2202,6 @@ s32 create_file(struct inode *inode, struct chain_t *p_dir, s32 ret, dentry, num_entries; struct dos_name_t dos_name; struct super_block *sb = inode->i_sb; - struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info); ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); @@ -2247,7 +2233,7 @@ s32 create_file(struct inode *inode, struct chain_t *p_dir, fid->entry = dentry; fid->attr = ATTR_ARCHIVE | mode; - fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->flags = 0x03; fid->size = 0; fid->start_clu = CLUSTER_32(~0); diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index b81d2a87b82e..1127131ee043 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -494,7 +494,7 @@ static int ffsGetVolInfo(struct super_block *sb, struct vol_info_t *info) if (p_fs->used_clusters == UINT_MAX) p_fs->used_clusters = exfat_count_used_clusters(sb); - info->FatType = p_fs->vol_type; + info->FatType = EXFAT; info->ClusterSize = p_fs->cluster_size; info->NumClusters = p_fs->num_clusters - 2; /* clu 0 & 1 */ info->UsedClusters = p_fs->used_clusters; @@ -602,7 +602,7 @@ static int ffsLookupFile(struct inode *inode, char *path, struct file_id_t *fid) fid->size = exfat_get_entry_size(ep2); if ((fid->type == TYPE_FILE) && (fid->size == 0)) { - fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->flags = 0x03; fid->start_clu = CLUSTER_32(~0); } else { fid->flags = exfat_get_entry_flag(ep2); @@ -1095,7 +1095,7 @@ static int ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) fid->size = new_size; fid->attr |= ATTR_ARCHIVE; if (new_size == 0) { - fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->flags = 0x03; fid->start_clu = CLUSTER_32(~0); } @@ -1203,14 +1203,6 @@ static int ffsMoveFile(struct inode *old_parent_inode, struct file_id_t *fid, dentry = fid->entry; - /* check if the old file is "." or ".." */ - if (p_fs->vol_type != EXFAT) { - if ((olddir.dir != p_fs->root_dir) && (dentry < 2)) { - ret = -EPERM; - goto out2; - } - } - ep = get_entry_in_dir(sb, &olddir, dentry, NULL); if (!ep) { ret = -ENOENT; @@ -1342,7 +1334,7 @@ static int ffsRemoveFile(struct inode *inode, struct file_id_t *fid) fid->size = 0; fid->start_clu = CLUSTER_32(~0); - fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->flags = 0x03; #ifndef CONFIG_STAGING_EXFAT_DELAYED_SYNC fs_sync(sb, true); @@ -2020,12 +2012,6 @@ static int ffsRemoveDir(struct inode *inode, struct file_id_t *fid) dentry = fid->entry; - /* check if the file is "." or ".." */ - if (p_fs->vol_type != EXFAT) { - if ((dir.dir != p_fs->root_dir) && (dentry < 2)) - return -EPERM; - } - /* acquire the lock for file system critical section */ mutex_lock(&p_fs->v_mutex); @@ -2048,7 +2034,7 @@ static int ffsRemoveDir(struct inode *inode, struct file_id_t *fid) fid->size = 0; fid->start_clu = CLUSTER_32(~0); - fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->flags = 0x03; #ifndef CONFIG_STAGING_EXFAT_DELAYED_SYNC fs_sync(sb, true); @@ -2073,8 +2059,6 @@ static int exfat_readdir(struct file *filp, struct dir_context *ctx) { struct inode *inode = file_inode(filp); struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct fs_info_t *p_fs = &sbi->fs_info; struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info); struct dir_entry_t de; unsigned long inum; @@ -2084,24 +2068,22 @@ static int exfat_readdir(struct file *filp, struct dir_context *ctx) __lock_super(sb); cpos = ctx->pos; - /* Fake . and .. for the root directory. */ - if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) { - while (cpos < 2) { - if (inode->i_ino == EXFAT_ROOT_INO) - inum = EXFAT_ROOT_INO; - else if (cpos == 0) - inum = inode->i_ino; - else /* (cpos == 1) */ - inum = parent_ino(filp->f_path.dentry); + /* Fake . and .. for any directory. */ + while (cpos < 2) { + if (inode->i_ino == EXFAT_ROOT_INO) + inum = EXFAT_ROOT_INO; + else if (cpos == 0) + inum = inode->i_ino; + else /* (cpos == 1) */ + inum = parent_ino(filp->f_path.dentry); - if (!dir_emit_dots(filp, ctx)) - goto out; - cpos++; - ctx->pos++; - } - if (cpos == 2) - cpos = 0; + if (!dir_emit_dots(filp, ctx)) + goto out; + cpos++; + ctx->pos++; } + if (cpos == 2) + cpos = 0; if (cpos & (DENTRY_SIZE - 1)) { err = -ENOENT; goto out; @@ -3345,7 +3327,7 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) return -EIO; } else { - info.FatType = p_fs->vol_type; + info.FatType = EXFAT; info.ClusterSize = p_fs->cluster_size; info.NumClusters = p_fs->num_clusters - 2; info.UsedClusters = p_fs->used_clusters; From 8b8822e32f35fffc4154a8cbaeb76974e4ab5344 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Thu, 30 Jan 2020 15:41:17 +0530 Subject: [PATCH 0016/2231] staging: exfat: Remove unused struct 'part_info_t' Remove global declaration of unused struct "part_info_t". Structure "part_info_t" is defined in exfat.h and not referenced in any other file. Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200130101118.15936-2-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 28d245b10e82..7d9bfea7a512 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -231,11 +231,6 @@ struct date_time_t { u16 MilliSecond; }; -struct part_info_t { - u32 Offset; /* start sector number of the partition */ - u32 Size; /* in sectors */ -}; - struct dev_info_t { u32 SecSize; /* sector size in bytes */ u32 DevSize; /* block device size in sectors */ From a3583d4c3df59e829e4d7f7e2ee6152dcda6abb4 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Thu, 30 Jan 2020 15:41:18 +0530 Subject: [PATCH 0017/2231] staging: exfat: Remove unused struct 'dev_info_t' Remove global declaration of unused struct "dev_info_t". Structure "dev_info_t" is defined in exfat.h and not referenced in any other file. Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200130101118.15936-3-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 7d9bfea7a512..e340b36ac519 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -231,11 +231,6 @@ struct date_time_t { u16 MilliSecond; }; -struct dev_info_t { - u32 SecSize; /* sector size in bytes */ - u32 DevSize; /* block device size in sectors */ -}; - struct vol_info_t { u32 FatType; u32 ClusterSize; From f47e8780ce749a4b96a360f7e44df511edca1f17 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Sat, 1 Feb 2020 19:58:08 +0530 Subject: [PATCH 0018/2231] staging: netlogic: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header files related to Netlogic network devices. It assigns explicit block comment to the SPDX License Identifier. Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Link: https://lore.kernel.org/r/20200201142804.GA12814@nishad Signed-off-by: Greg Kroah-Hartman --- drivers/staging/netlogic/platform_net.h | 4 ++-- drivers/staging/netlogic/xlr_net.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/netlogic/platform_net.h b/drivers/staging/netlogic/platform_net.h index f152d84099a2..c8d4c13424c6 100644 --- a/drivers/staging/netlogic/platform_net.h +++ b/drivers/staging/netlogic/platform_net.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) - * +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ +/* * Copyright (c) 2003-2012 Broadcom Corporation * All Rights Reserved */ diff --git a/drivers/staging/netlogic/xlr_net.h b/drivers/staging/netlogic/xlr_net.h index 518ea809b8fa..8365b744f9b3 100644 --- a/drivers/staging/netlogic/xlr_net.h +++ b/drivers/staging/netlogic/xlr_net.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) - * +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ +/* * Copyright (c) 2003-2012 Broadcom Corporation * All Rights Reserved */ From b99692f4db0b3d42266c5b95a6b7860c35b596bb Mon Sep 17 00:00:00 2001 From: Derek Robson Date: Sun, 2 Feb 2020 20:02:43 +1300 Subject: [PATCH 0019/2231] staging: rtl8192e: style fix - Prefer using '"%s...", __func__' Fixed checkpatch warnings, Prefer using '"%s...", __func__' Signed-off-by: Derek Robson Link: https://lore.kernel.org/r/20200202070243.112059-1-robsonde@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../staging/rtl8192e/rtl8192e/r8192E_dev.c | 10 +++--- .../staging/rtl8192e/rtl8192e/r8192E_phy.c | 36 +++++++------------ drivers/staging/rtl8192e/rtl8192e/rtl_cam.c | 6 ++-- drivers/staging/rtl8192e/rtl8192e/rtl_core.c | 24 ++++++------- drivers/staging/rtl8192e/rtl819x_BAProc.c | 14 ++++---- drivers/staging/rtl8192e/rtl819x_HTProc.c | 2 +- drivers/staging/rtl8192e/rtl819x_TSProc.c | 2 +- drivers/staging/rtl8192e/rtllib_tx.c | 2 +- 8 files changed, 43 insertions(+), 53 deletions(-) diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c index 980b850d729a..ddcd7885d190 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c @@ -304,7 +304,7 @@ static void _rtl92e_read_eeprom_info(struct net_device *dev) u16 i, usValue, IC_Version; u16 EEPROMId; - RT_TRACE(COMP_INIT, "====> _rtl92e_read_eeprom_info\n"); + RT_TRACE(COMP_INIT, "====> %s\n", __func__); EEPROMId = rtl92e_eeprom_read(dev, 0); if (EEPROMId != RTL8190_EEPROM_ID) { @@ -1354,8 +1354,8 @@ static u8 _rtl92e_rate_hw_to_mgn(bool bIsHT, u8 rate) default: RT_TRACE(COMP_RECV, - "_rtl92e_rate_hw_to_mgn(): Non supportedRate [%x], bIsHT = %d!!!\n", - rate, bIsHT); + "%s: Non supportedRate [%x], bIsHT = %d!!!\n", + __func__, rate, bIsHT); break; } @@ -1415,8 +1415,8 @@ static u8 _rtl92e_rate_hw_to_mgn(bool bIsHT, u8 rate) default: RT_TRACE(COMP_RECV, - "_rtl92e_rate_hw_to_mgn(): Non supported Rate [%x], bIsHT = %d!!!\n", - rate, bIsHT); + "%s: Non supported Rate [%x], bIsHT = %d!!!\n", + __func__, rate, bIsHT); break; } } diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c index 7d78f16efc1d..411138102948 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c @@ -1124,14 +1124,14 @@ static void _rtl92e_cck_tx_power_track_bw_switch_thermal(struct net_device *dev) priv->Record_CCK_20Mindex = 6; priv->CCK_index = priv->Record_CCK_20Mindex; RT_TRACE(COMP_POWER_TRACKING, - "20MHz, _rtl92e_cck_tx_power_track_bw_switch_thermal(),CCK_index = %d\n", + "20MHz, %s,CCK_index = %d\n", __func__, priv->CCK_index); break; case HT_CHANNEL_WIDTH_20_40: priv->CCK_index = priv->Record_CCK_40Mindex; RT_TRACE(COMP_POWER_TRACKING, - "40MHz, _rtl92e_cck_tx_power_track_bw_switch_thermal(), CCK_index = %d\n", + "40MHz, %s, CCK_index = %d\n", __func__, priv->CCK_index); break; } @@ -1155,7 +1155,7 @@ static void _rtl92e_set_bw_mode_work_item(struct net_device *dev) u8 regBwOpMode; RT_TRACE(COMP_SWBW, - "==>_rtl92e_set_bw_mode_work_item() Switch to %s bandwidth\n", + "==>%s Switch to %s bandwidth\n", __func__, priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20 ? "20MHz" : "40MHz"); @@ -1416,15 +1416,14 @@ static bool _rtl92e_set_rf_power_state(struct net_device *dev, if (priv->SetRFPowerStateInProgress) return false; - RT_TRACE(COMP_PS, "===========> _rtl92e_set_rf_power_state()!\n"); + RT_TRACE(COMP_PS, "===========> %s!\n", __func__); priv->SetRFPowerStateInProgress = true; switch (priv->rf_chip) { case RF_8256: switch (eRFPowerState) { case eRfOn: - RT_TRACE(COMP_PS, - "_rtl92e_set_rf_power_state() eRfOn!\n"); + RT_TRACE(COMP_PS, "%s eRfOn!\n", __func__); if ((priv->rtllib->eRFPowerState == eRfOff) && RT_IN_PS_LEVEL(pPSC, RT_RF_OFF_LEVL_HALT_NIC)) { bool rtstatus; @@ -1490,10 +1489,8 @@ static bool _rtl92e_set_rf_power_state(struct net_device *dev, } if (i >= MAX_DOZE_WAITING_TIMES_9x) { - RT_TRACE(COMP_POWER, - "\n\n\n TimeOut!! _rtl92e_set_rf_power_state(): eRfOff: %d times TcbBusyQueue[%d] != 0 !!!\n", - MAX_DOZE_WAITING_TIMES_9x, - QueueID); + RT_TRACE(COMP_POWER, "\n\n\n TimeOut!! %s: eRfOff: %d times TcbBusyQueue[%d] != 0 !!!\n", + __func__, MAX_DOZE_WAITING_TIMES_9x, QueueID); break; } } @@ -1501,8 +1498,7 @@ static bool _rtl92e_set_rf_power_state(struct net_device *dev, break; case eRfOff: - RT_TRACE(COMP_PS, - "_rtl92e_set_rf_power_state() eRfOff/Sleep !\n"); + RT_TRACE(COMP_PS, "%s eRfOff/Sleep !\n", __func__); for (QueueID = 0, i = 0; QueueID < MAX_TX_QUEUE; ) { ring = &priv->tx_ring[QueueID]; @@ -1567,9 +1563,7 @@ static bool _rtl92e_set_rf_power_state(struct net_device *dev, } priv->SetRFPowerStateInProgress = false; - RT_TRACE(COMP_PS, - "<=========== _rtl92e_set_rf_power_state() bResult = %d!\n", - bResult); + RT_TRACE(COMP_PS, "<=========== %s bResult = %d!\n", __func__, bResult); return bResult; } @@ -1581,21 +1575,17 @@ bool rtl92e_set_rf_power_state(struct net_device *dev, bool bResult = false; RT_TRACE(COMP_PS, - "---------> rtl92e_set_rf_power_state(): eRFPowerState(%d)\n", - eRFPowerState); + "---------> %s: eRFPowerState(%d)\n", __func__, eRFPowerState); if (eRFPowerState == priv->rtllib->eRFPowerState && priv->bHwRfOffAction == 0) { - RT_TRACE(COMP_PS, - "<--------- rtl92e_set_rf_power_state(): discard the request for eRFPowerState(%d) is the same.\n", - eRFPowerState); + RT_TRACE(COMP_PS, "<--------- %s: discard the request for eRFPowerState(%d) is the same.\n", + __func__, eRFPowerState); return bResult; } bResult = _rtl92e_set_rf_power_state(dev, eRFPowerState); - RT_TRACE(COMP_PS, - "<--------- rtl92e_set_rf_power_state(): bResult(%d)\n", - bResult); + RT_TRACE(COMP_PS, "<--------- %s: bResult(%d)\n", __func__, bResult); return bResult; } diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c index 627ea1029509..c8506517cc8d 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c @@ -108,8 +108,8 @@ void rtl92e_set_key(struct net_device *dev, u8 EntryNo, u8 KeyIndex, } RT_TRACE(COMP_SEC, - "====>to rtl92e_set_key(), dev:%p, EntryNo:%d, KeyIndex:%d,KeyType:%d, MacAddr %pM\n", - dev, EntryNo, KeyIndex, KeyType, MacAddr); + "====>to %s, dev:%p, EntryNo:%d, KeyIndex:%d,KeyType:%d, MacAddr %pM\n", + __func__, dev, EntryNo, KeyIndex, KeyType, MacAddr); if (DefaultKey) usConfig |= BIT15 | (KeyType<<2); @@ -163,7 +163,7 @@ void rtl92e_cam_restore(struct net_device *dev) 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - RT_TRACE(COMP_SEC, "rtl92e_cam_restore:\n"); + RT_TRACE(COMP_SEC, "%s:\n", __func__); if ((priv->rtllib->pairwise_key_type == KEY_TYPE_WEP40) || diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index 11183b9f757a..ce57bf35c3ed 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -145,21 +145,21 @@ bool rtl92e_set_rf_state(struct net_device *dev, unsigned long flag; RT_TRACE((COMP_PS | COMP_RF), - "===>rtl92e_set_rf_state(): StateToSet(%d)\n", StateToSet); + "===>%s: StateToSet(%d)\n", __func__, StateToSet); while (true) { spin_lock_irqsave(&priv->rf_ps_lock, flag); if (priv->RFChangeInProgress) { spin_unlock_irqrestore(&priv->rf_ps_lock, flag); RT_TRACE((COMP_PS | COMP_RF), - "rtl92e_set_rf_state(): RF Change in progress! Wait to set..StateToSet(%d).\n", - StateToSet); + "%s: RF Change in progress! Wait to set..StateToSet(%d).\n", + __func__, StateToSet); while (priv->RFChangeInProgress) { RFWaitCounter++; RT_TRACE((COMP_PS | COMP_RF), - "rtl92e_set_rf_state(): Wait 1 ms (%d times)...\n", - RFWaitCounter); + "%s: Wait 1 ms (%d times)...\n", + __func__, RFWaitCounter); mdelay(1); if (RFWaitCounter > 100) { @@ -195,8 +195,8 @@ bool rtl92e_set_rf_state(struct net_device *dev, bConnectBySSID = true; } else { RT_TRACE((COMP_PS | COMP_RF), - "rtl92e_set_rf_state - eRfon reject pMgntInfo->RfOffReason= 0x%x, ChangeSource=0x%X\n", - priv->rtllib->RfOffReason, ChangeSource); + "%s - eRfon reject pMgntInfo->RfOffReason= 0x%x, ChangeSource=0x%X\n", + __func__, priv->rtllib->RfOffReason, ChangeSource); } break; @@ -232,8 +232,8 @@ bool rtl92e_set_rf_state(struct net_device *dev, if (bActionAllowed) { RT_TRACE((COMP_PS | COMP_RF), - "rtl92e_set_rf_state(): Action is allowed.... StateToSet(%d), RfOffReason(%#X)\n", - StateToSet, priv->rtllib->RfOffReason); + "%s: Action is allowed.... StateToSet(%d), RfOffReason(%#X)\n", + __func__, StateToSet, priv->rtllib->RfOffReason); PHY_SetRFPowerState(dev, StateToSet); if (StateToSet == eRfOn) { @@ -245,15 +245,15 @@ bool rtl92e_set_rf_state(struct net_device *dev, } } else { RT_TRACE((COMP_PS | COMP_RF), - "rtl92e_set_rf_state(): Action is rejected.... StateToSet(%d), ChangeSource(%#X), RfOffReason(%#X)\n", - StateToSet, ChangeSource, priv->rtllib->RfOffReason); + "%s: Action is rejected.... StateToSet(%d), ChangeSource(%#X), RfOffReason(%#X)\n", + __func__, StateToSet, ChangeSource, priv->rtllib->RfOffReason); } spin_lock_irqsave(&priv->rf_ps_lock, flag); priv->RFChangeInProgress = false; spin_unlock_irqrestore(&priv->rf_ps_lock, flag); - RT_TRACE((COMP_PS | COMP_RF), "<===rtl92e_set_rf_state()\n"); + RT_TRACE((COMP_PS | COMP_RF), "<===%s\n", __func__); return bActionAllowed; } diff --git a/drivers/staging/rtl8192e/rtl819x_BAProc.c b/drivers/staging/rtl8192e/rtl819x_BAProc.c index 816d31c1d5c7..2d5e4a0330c6 100644 --- a/drivers/staging/rtl8192e/rtl819x_BAProc.c +++ b/drivers/staging/rtl8192e/rtl819x_BAProc.c @@ -119,8 +119,8 @@ static struct sk_buff *rtllib_ADDBA(struct rtllib_device *ieee, u8 *Dst, } #ifdef VERBOSE_DEBUG - print_hex_dump_bytes("rtllib_ADDBA(): ", DUMP_PREFIX_NONE, skb->data, - skb->len); + print_hex_dump_bytes("%s: ", DUMP_PREFIX_NONE, skb->data, + __func__, skb->len); #endif return skb; } @@ -170,8 +170,8 @@ static struct sk_buff *rtllib_DELBA(struct rtllib_device *ieee, u8 *dst, tag += 2; #ifdef VERBOSE_DEBUG - print_hex_dump_bytes("rtllib_DELBA(): ", DUMP_PREFIX_NONE, skb->data, - skb->len); + print_hex_dump_bytes("%s: ", DUMP_PREFIX_NONE, skb->data, + __func__, skb->len); #endif return skb; } @@ -235,7 +235,7 @@ int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb) } #ifdef VERBOSE_DEBUG - print_hex_dump_bytes("rtllib_rx_ADDBAReq(): ", DUMP_PREFIX_NONE, + print_hex_dump_bytes("%s: ", DUMP_PREFIX_NONE, __func__, skb->data, skb->len); #endif @@ -433,8 +433,8 @@ int rtllib_rx_DELBA(struct rtllib_device *ieee, struct sk_buff *skb) } #ifdef VERBOSE_DEBUG - print_hex_dump_bytes("rtllib_rx_DELBA(): ", DUMP_PREFIX_NONE, skb->data, - skb->len); + print_hex_dump_bytes("%s: ", DUMP_PREFIX_NONE, skb->data, + __func__, skb->len); #endif delba = (struct rtllib_hdr_3addr *)skb->data; dst = (u8 *)(&delba->addr2[0]); diff --git a/drivers/staging/rtl8192e/rtl819x_HTProc.c b/drivers/staging/rtl8192e/rtl819x_HTProc.c index f02263af9624..d83d72594312 100644 --- a/drivers/staging/rtl8192e/rtl819x_HTProc.c +++ b/drivers/staging/rtl8192e/rtl819x_HTProc.c @@ -545,7 +545,7 @@ void HTOnAssocRsp(struct rtllib_device *ieee) #ifdef VERBOSE_DEBUG - print_hex_dump_bytes("HTOnAssocRsp(): ", DUMP_PREFIX_NONE, + print_hex_dump_bytes("%s: ", __func__, DUMP_PREFIX_NONE, pPeerHTCap, sizeof(struct ht_capab_ele)); #endif HTSetConnectBwMode(ieee, (enum ht_channel_width)(pPeerHTCap->ChlWidth), diff --git a/drivers/staging/rtl8192e/rtl819x_TSProc.c b/drivers/staging/rtl8192e/rtl819x_TSProc.c index 672bf0987943..47b2669a3a8e 100644 --- a/drivers/staging/rtl8192e/rtl819x_TSProc.c +++ b/drivers/staging/rtl8192e/rtl819x_TSProc.c @@ -440,7 +440,7 @@ void RemovePeerTS(struct rtllib_device *ieee, u8 *Addr) { struct ts_common_info *pTS, *pTmpTS; - netdev_info(ieee->dev, "===========>RemovePeerTS, %pM\n", Addr); + netdev_info(ieee->dev, "===========>%s, %pM\n", __func__, Addr); list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List) { if (memcmp(pTS->Addr, Addr, 6) == 0) { diff --git a/drivers/staging/rtl8192e/rtllib_tx.c b/drivers/staging/rtl8192e/rtllib_tx.c index 8cddb2e12dc4..79d7ad7c0a4a 100644 --- a/drivers/staging/rtl8192e/rtllib_tx.c +++ b/drivers/staging/rtl8192e/rtllib_tx.c @@ -241,7 +241,7 @@ static int rtllib_classify(struct sk_buff *skb, u8 bIsAmsdu) return 0; #ifdef VERBOSE_DEBUG - print_hex_dump_bytes("rtllib_classify(): ", DUMP_PREFIX_NONE, skb->data, + print_hex_dump_bytes("%s: ", __func__, DUMP_PREFIX_NONE, skb->data, skb->len); #endif ip = ip_hdr(skb); From 361906387c7decfe99d4a99d6218c39f47c56406 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:17 +0100 Subject: [PATCH 0020/2231] staging: vc04_services: Remove unused variables in struct vchiq_arm_state There are not being used, so we're better off without them. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-2-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 19d2a2eefb6a..f0044289b6bc 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -53,8 +53,6 @@ struct vchiq_arm_state { enum vc_suspend_status vc_suspend_state; enum vc_resume_status vc_resume_state; - unsigned int wake_address; - struct vchiq_state *state; struct timer_list suspend_timer; int suspend_timer_timeout; @@ -80,19 +78,11 @@ struct vchiq_arm_state { struct completion blocked_blocker; int blocked_count; - int autosuspend_override; - /* Flag to indicate that the first vchiq connect has made it through. ** This means that both sides should be fully ready, and we should ** be able to suspend after this point. */ int first_connect; - - unsigned long long suspend_start_time; - unsigned long long sleep_start_time; - unsigned long long resume_start_time; - unsigned long long last_wake_time; - }; struct vchiq_drvdata { From 7d153fcc10702fbc5e96c8bb83832c166cd813d4 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:18 +0100 Subject: [PATCH 0021/2231] staging: vc04_services: Get rid of resume_blocked in struct vchiq_arm_state The boolean value is never set to true, hence remove it and all the code that depends on it. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-3-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_arm.c | 51 +------------------ .../interface/vchiq_arm/vchiq_arm.h | 1 - 2 files changed, 2 insertions(+), 50 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 4458c1e60fa3..af4dc23f5510 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2270,10 +2270,7 @@ vchiq_videocore_wanted(struct vchiq_state *state) return 1; else if (!arm_state->videocore_use_count) /* usage count zero - check for override unless we're forcing */ - if (arm_state->resume_blocked) - return 0; - else - return vchiq_platform_videocore_wanted(state); + return vchiq_platform_videocore_wanted(state); else /* non-zero usage count - videocore still required */ return 1; @@ -2567,7 +2564,6 @@ static inline void unblock_resume(struct vchiq_arm_state *arm_state) { complete_all(&arm_state->resume_blocker); - arm_state->resume_blocked = 0; } /* Initiate suspend via slot handler. Should be called with the write lock @@ -2724,46 +2720,6 @@ vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, } write_lock_bh(&arm_state->susp_res_lock); - while (arm_state->resume_blocked) { - /* If we call 'use' while force suspend is waiting for suspend, - * then we're about to block the thread which the force is - * waiting to complete, so we're bound to just time out. In this - * case, set the suspend state such that the wait will be - * canceled, so we can complete as quickly as possible. */ - if (arm_state->resume_blocked && arm_state->vc_suspend_state == - VC_SUSPEND_IDLE) { - set_suspend_state(arm_state, VC_SUSPEND_FORCE_CANCELED); - break; - } - /* If suspend is already in progress then we need to block */ - if (!try_wait_for_completion(&arm_state->resume_blocker)) { - /* Indicate that there are threads waiting on the resume - * blocker. These need to be allowed to complete before - * a _second_ call to force suspend can complete, - * otherwise low priority threads might never actually - * continue */ - arm_state->blocked_count++; - write_unlock_bh(&arm_state->susp_res_lock); - vchiq_log_info(vchiq_susp_log_level, "%s %s resume " - "blocked - waiting...", __func__, entity); - if (wait_for_completion_killable( - &arm_state->resume_blocker)) { - vchiq_log_error(vchiq_susp_log_level, "%s %s " - "wait for resume blocker interrupted", - __func__, entity); - ret = VCHIQ_ERROR; - write_lock_bh(&arm_state->susp_res_lock); - arm_state->blocked_count--; - write_unlock_bh(&arm_state->susp_res_lock); - goto out; - } - vchiq_log_info(vchiq_susp_log_level, "%s %s resume " - "unblocked", __func__, entity); - write_lock_bh(&arm_state->susp_res_lock); - if (--arm_state->blocked_count == 0) - complete_all(&arm_state->blocked_blocker); - } - } stop_suspend_timer(arm_state); @@ -2861,10 +2817,7 @@ vchiq_release_internal(struct vchiq_state *state, struct vchiq_service *service) --(*entity_uc); if (!vchiq_videocore_wanted(state)) { - if (vchiq_platform_use_suspend_timer() && - !arm_state->resume_blocked) { - /* Only use the timer if we're not trying to force - * suspend (=> resume_blocked) */ + if (vchiq_platform_use_suspend_timer()) { start_suspend_timer(arm_state); } else { vchiq_log_info(vchiq_susp_log_level, diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index f0044289b6bc..c904f7be9084 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -74,7 +74,6 @@ struct vchiq_arm_state { ** ARM is suspending */ struct completion resume_blocker; - int resume_blocked; struct completion blocked_blocker; int blocked_count; From d91f500a12739cfbece9820a0e243ed9cecac8cc Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:19 +0100 Subject: [PATCH 0022/2231] staging: vc04_services: Get rid of resume_blocker completion in struct vchiq_arm_state Nobody is waiting on it, so delete all relevant code. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-4-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 11 ----------- .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 4 ---- 2 files changed, 15 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index af4dc23f5510..311df3d85494 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2386,11 +2386,6 @@ vchiq_arm_init_state(struct vchiq_state *state, * completion while videocore is suspended. */ set_resume_state(arm_state, VC_RESUME_RESUMED); - init_completion(&arm_state->resume_blocker); - /* Initialise to 'done' state. We only want to block on this - * completion while resume is blocked */ - complete_all(&arm_state->resume_blocker); - init_completion(&arm_state->blocked_blocker); /* Initialise to 'done' state. We only want to block on this * completion while things are waiting on the resume blocker */ @@ -2560,12 +2555,6 @@ need_resume(struct vchiq_state *state) vchiq_videocore_wanted(state); } -static inline void -unblock_resume(struct vchiq_arm_state *arm_state) -{ - complete_all(&arm_state->resume_blocker); -} - /* Initiate suspend via slot handler. Should be called with the write lock * held */ enum vchiq_status diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index c904f7be9084..7d1316875343 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -70,10 +70,6 @@ struct vchiq_arm_state { */ int peer_use_count; - /* Flag to indicate whether resume is blocked. This happens when the - ** ARM is suspending - */ - struct completion resume_blocker; struct completion blocked_blocker; int blocked_count; From 9af64167354fc62eb253c802029313e7f29291d3 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:20 +0100 Subject: [PATCH 0023/2231] staging: vc04_services: get rid of blocked_blocker completion in struct vchiq_arm_state Nobody is waiting on it, so delete it. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-5-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 5 ----- .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.h | 1 - 2 files changed, 6 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 311df3d85494..9264bb5d0bbe 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2386,11 +2386,6 @@ vchiq_arm_init_state(struct vchiq_state *state, * completion while videocore is suspended. */ set_resume_state(arm_state, VC_RESUME_RESUMED); - init_completion(&arm_state->blocked_blocker); - /* Initialise to 'done' state. We only want to block on this - * completion while things are waiting on the resume blocker */ - complete_all(&arm_state->blocked_blocker); - arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS; arm_state->suspend_timer_running = 0; arm_state->state = state; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 7d1316875343..6bab241313b8 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -70,7 +70,6 @@ struct vchiq_arm_state { */ int peer_use_count; - struct completion blocked_blocker; int blocked_count; /* Flag to indicate that the first vchiq connect has made it through. From dcbcbfa4af0964202d38f23f21072e617448acaa Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:21 +0100 Subject: [PATCH 0024/2231] staging: vc04_services: Delete blocked_count in struct vchiq_arm_state The variable is always 0. So delete it an all the code conditional to it. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-6-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 2 -- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 9264bb5d0bbe..4545df573c90 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2266,8 +2266,6 @@ vchiq_videocore_wanted(struct vchiq_state *state) if (!arm_state) /* autosuspend not supported - always return wanted */ return 1; - else if (arm_state->blocked_count) - return 1; else if (!arm_state->videocore_use_count) /* usage count zero - check for override unless we're forcing */ return vchiq_platform_videocore_wanted(state); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 6bab241313b8..35889a65b17f 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -70,8 +70,6 @@ struct vchiq_arm_state { */ int peer_use_count; - int blocked_count; - /* Flag to indicate that the first vchiq connect has made it through. ** This means that both sides should be fully ready, and we should ** be able to suspend after this point. From be7fbad631f35137bc966e0fc6f00ff34a041d05 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:22 +0100 Subject: [PATCH 0025/2231] staging: vc04_services: get rid of vchiq_platform_use_suspend_timer() The function always returns 0, delete the function and all code conditional to it, namely the suspend timer. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-7-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_2835_arm.c | 11 --- .../interface/vchiq_arm/vchiq_arm.c | 83 ++----------------- .../interface/vchiq_arm/vchiq_arm.h | 9 -- 3 files changed, 5 insertions(+), 98 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index ca30bfd52919..1ffb2aea947c 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -284,17 +284,6 @@ vchiq_platform_videocore_wanted(struct vchiq_state *state) { return 1; // autosuspend not supported - videocore always wanted } - -int -vchiq_platform_use_suspend_timer(void) -{ - return 0; -} -void -vchiq_dump_platform_use_state(struct vchiq_state *state) -{ - vchiq_log_info(vchiq_arm_log_level, "Suspend timer not in use"); -} void vchiq_platform_handle_timeout(struct vchiq_state *state) { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 4545df573c90..2dfa5793d83b 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -48,9 +48,6 @@ int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; int vchiq_susp_log_level = VCHIQ_LOG_ERROR; -#define SUSPEND_TIMER_TIMEOUT_MS 100 -#define SUSPEND_RETRY_TIMER_TIMEOUT_MS 1000 - #define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */ static const char *const suspend_state_names[] = { "VC_SUSPEND_FORCE_CANCELED", @@ -79,8 +76,6 @@ static const char *const resume_state_names[] = { * requested */ #define FORCE_SUSPEND_TIMEOUT_MS 200 -static void suspend_timer_callback(struct timer_list *t); - struct user_service { struct vchiq_service *service; void *userdata; @@ -2384,12 +2379,7 @@ vchiq_arm_init_state(struct vchiq_state *state, * completion while videocore is suspended. */ set_resume_state(arm_state, VC_RESUME_RESUMED); - arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS; - arm_state->suspend_timer_running = 0; arm_state->state = state; - timer_setup(&arm_state->suspend_timer, suspend_timer_callback, - 0); - arm_state->first_connect = 0; } @@ -2517,27 +2507,6 @@ set_resume_state(struct vchiq_arm_state *arm_state, } } -/* should be called with the write lock held */ -inline void -start_suspend_timer(struct vchiq_arm_state *arm_state) -{ - del_timer(&arm_state->suspend_timer); - arm_state->suspend_timer.expires = jiffies + - msecs_to_jiffies(arm_state->suspend_timer_timeout); - add_timer(&arm_state->suspend_timer); - arm_state->suspend_timer_running = 1; -} - -/* should be called with the write lock held */ -static inline void -stop_suspend_timer(struct vchiq_arm_state *arm_state) -{ - if (arm_state->suspend_timer_running) { - del_timer(&arm_state->suspend_timer); - arm_state->suspend_timer_running = 0; - } -} - static inline int need_resume(struct vchiq_state *state) { @@ -2626,28 +2595,6 @@ out: return; } -void -vchiq_check_suspend(struct vchiq_state *state) -{ - struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); - - if (!arm_state) - goto out; - - vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); - - write_lock_bh(&arm_state->susp_res_lock); - if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED && - arm_state->first_connect && - !vchiq_videocore_wanted(state)) { - vchiq_arm_vcsuspend(state); - } - write_unlock_bh(&arm_state->susp_res_lock); - -out: - vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -} - /* This function should be called with the write lock held */ int vchiq_check_resume(struct vchiq_state *state) @@ -2702,9 +2649,6 @@ vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, } write_lock_bh(&arm_state->susp_res_lock); - - stop_suspend_timer(arm_state); - local_uc = ++arm_state->videocore_use_count; local_entity_uc = ++(*entity_uc); @@ -2799,15 +2743,11 @@ vchiq_release_internal(struct vchiq_state *state, struct vchiq_service *service) --(*entity_uc); if (!vchiq_videocore_wanted(state)) { - if (vchiq_platform_use_suspend_timer()) { - start_suspend_timer(arm_state); - } else { - vchiq_log_info(vchiq_susp_log_level, - "%s %s count %d, state count %d - suspending", - __func__, entity, *entity_uc, - arm_state->videocore_use_count); - vchiq_arm_vcsuspend(state); - } + vchiq_log_info(vchiq_susp_log_level, + "%s %s count %d, state count %d - suspending", + __func__, entity, *entity_uc, + arm_state->videocore_use_count); + vchiq_arm_vcsuspend(state); } else vchiq_log_trace(vchiq_susp_log_level, "%s %s count %d, state count %d", @@ -2902,17 +2842,6 @@ vchiq_instance_set_trace(struct vchiq_instance *instance, int trace) instance->trace = (trace != 0); } -static void suspend_timer_callback(struct timer_list *t) -{ - struct vchiq_arm_state *arm_state = - from_timer(arm_state, t, suspend_timer); - struct vchiq_state *state = arm_state->state; - - vchiq_log_info(vchiq_susp_log_level, - "%s - suspend timer expired - check suspend", __func__); - vchiq_check_suspend(state); -} - enum vchiq_status vchiq_use_service(unsigned int handle) { @@ -3028,8 +2957,6 @@ vchiq_dump_service_use_state(struct vchiq_state *state) "--- Overall vchiq instance use count %d", vc_use_count); kfree(service_data); - - vchiq_dump_platform_use_state(state); } enum vchiq_status diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 35889a65b17f..61c50d7d4396 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -54,9 +54,6 @@ struct vchiq_arm_state { enum vc_resume_status vc_resume_state; struct vchiq_state *state; - struct timer_list suspend_timer; - int suspend_timer_timeout; - int suspend_timer_running; /* Global use count for videocore. ** This is equal to the sum of the use counts for all services. When @@ -121,9 +118,6 @@ vchiq_platform_suspend(struct vchiq_state *state); extern int vchiq_platform_videocore_wanted(struct vchiq_state *state); -extern int -vchiq_platform_use_suspend_timer(void); - extern void vchiq_dump_platform_use_state(struct vchiq_state *state); @@ -166,7 +160,4 @@ extern void set_resume_state(struct vchiq_arm_state *arm_state, enum vc_resume_status new_state); -extern void -start_suspend_timer(struct vchiq_arm_state *arm_state); - #endif /* VCHIQ_ARM_H */ From 0e26621250627a62f2aabc511a1686f962dd8b24 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:23 +0100 Subject: [PATCH 0026/2231] staging: vc04_services: Get rid of vchiq_platform_paused/resumed() vchiq_platform_paused() and vchiq_platform_resumed() do nothing. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-8-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_2835_arm.c | 10 ---------- .../vc04_services/interface/vchiq_arm/vchiq_core.c | 3 --- .../vc04_services/interface/vchiq_arm/vchiq_core.h | 6 ------ 3 files changed, 19 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index 1ffb2aea947c..5f59145f251b 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -269,16 +269,6 @@ vchiq_platform_resume(struct vchiq_state *state) return VCHIQ_SUCCESS; } -void -vchiq_platform_paused(struct vchiq_state *state) -{ -} - -void -vchiq_platform_resumed(struct vchiq_state *state) -{ -} - int vchiq_platform_videocore_wanted(struct vchiq_state *state) { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 76351078affb..71342826ed33 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -1798,7 +1798,6 @@ parse_rx_slots(struct vchiq_state *state) } /* At this point slot_mutex is held */ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED); - vchiq_platform_paused(state); break; case VCHIQ_MSG_RESUME: vchiq_log_trace(vchiq_core_log_level, @@ -1807,7 +1806,6 @@ parse_rx_slots(struct vchiq_state *state) /* Release the slot mutex */ mutex_unlock(&state->slot_mutex); vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); - vchiq_platform_resumed(state); break; case VCHIQ_MSG_REMOTE_USE: @@ -1908,7 +1906,6 @@ slot_handler_func(void *v) != VCHIQ_RETRY) { vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); - vchiq_platform_resumed(state); } else { /* This should really be impossible, ** since the PAUSE should have flushed diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index c31f953a9986..72c88fe5feb1 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -593,15 +593,9 @@ remote_event_signal(struct remote_event *event); void vchiq_platform_check_suspend(struct vchiq_state *state); -extern void -vchiq_platform_paused(struct vchiq_state *state); - extern enum vchiq_status vchiq_platform_resume(struct vchiq_state *state); -extern void -vchiq_platform_resumed(struct vchiq_state *state); - extern int vchiq_dump(void *dump_context, const char *str, int len); From 88084848eb8c8ccda6987bf77992c5ce45abf1b9 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:24 +0100 Subject: [PATCH 0027/2231] staging: vc04_services: Get rid of vchiq_platform_suspend/resume() vchiq_platform_suspend() and vchiq_platform_resume() do nothing, get rid of them. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-9-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_2835_arm.c | 12 ------------ .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 8 +------- .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 3 --- .../vc04_services/interface/vchiq_arm/vchiq_core.c | 4 ---- .../vc04_services/interface/vchiq_arm/vchiq_core.h | 3 --- 5 files changed, 1 insertion(+), 29 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index 5f59145f251b..65e26a90c1db 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -257,18 +257,6 @@ int vchiq_dump_platform_state(void *dump_context) return vchiq_dump(dump_context, buf, len + 1); } -enum vchiq_status -vchiq_platform_suspend(struct vchiq_state *state) -{ - return VCHIQ_ERROR; -} - -enum vchiq_status -vchiq_platform_resume(struct vchiq_state *state) -{ - return VCHIQ_SUCCESS; -} - int vchiq_platform_videocore_wanted(struct vchiq_state *state) { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 2dfa5793d83b..850774ded8db 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2572,7 +2572,6 @@ void vchiq_platform_check_suspend(struct vchiq_state *state) { struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); - int susp = 0; if (!arm_state) goto out; @@ -2581,15 +2580,10 @@ vchiq_platform_check_suspend(struct vchiq_state *state) write_lock_bh(&arm_state->susp_res_lock); if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED && - arm_state->vc_resume_state == VC_RESUME_RESUMED) { + arm_state->vc_resume_state == VC_RESUME_RESUMED) set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS); - susp = 1; - } write_unlock_bh(&arm_state->susp_res_lock); - if (susp) - vchiq_platform_suspend(state); - out: vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); return; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 61c50d7d4396..1874a01f5201 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -112,9 +112,6 @@ vchiq_release_service(unsigned int handle); extern enum vchiq_status vchiq_check_service(struct vchiq_service *service); -extern enum vchiq_status -vchiq_platform_suspend(struct vchiq_state *state); - extern int vchiq_platform_videocore_wanted(struct vchiq_state *state); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 71342826ed33..ef8340ab8a52 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -1895,10 +1895,6 @@ slot_handler_func(void *v) } break; - case VCHIQ_CONNSTATE_PAUSED: - vchiq_platform_resume(state); - break; - case VCHIQ_CONNSTATE_RESUMING: if (queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 72c88fe5feb1..535a67cc68ed 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -593,9 +593,6 @@ remote_event_signal(struct remote_event *event); void vchiq_platform_check_suspend(struct vchiq_state *state); -extern enum vchiq_status -vchiq_platform_resume(struct vchiq_state *state); - extern int vchiq_dump(void *dump_context, const char *str, int len); From 004e890d5cf7d5b0945e8c7ffadd574e327e8e3d Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:25 +0100 Subject: [PATCH 0028/2231] staging: vc04_services: Get rid of vchiq_platform_videocore_wanted() The function always returns true, which makes a whole bunch of code useless. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-10-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_2835_arm.c | 5 --- .../interface/vchiq_arm/vchiq_arm.c | 34 +++---------------- .../interface/vchiq_arm/vchiq_arm.h | 5 --- 3 files changed, 5 insertions(+), 39 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index 65e26a90c1db..a7f72dba2e20 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -257,11 +257,6 @@ int vchiq_dump_platform_state(void *dump_context) return vchiq_dump(dump_context, buf, len + 1); } -int -vchiq_platform_videocore_wanted(struct vchiq_state *state) -{ - return 1; // autosuspend not supported - videocore always wanted -} void vchiq_platform_handle_timeout(struct vchiq_state *state) { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 850774ded8db..c147d94784eb 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2253,22 +2253,6 @@ vchiq_fops = { * Autosuspend related functionality */ -int -vchiq_videocore_wanted(struct vchiq_state *state) -{ - struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); - - if (!arm_state) - /* autosuspend not supported - always return wanted */ - return 1; - else if (!arm_state->videocore_use_count) - /* usage count zero - check for override unless we're forcing */ - return vchiq_platform_videocore_wanted(state); - else - /* non-zero usage count - videocore still required */ - return 1; -} - static enum vchiq_status vchiq_keepalive_vchiq_callback(enum vchiq_reason reason, struct vchiq_header *header, @@ -2513,8 +2497,7 @@ need_resume(struct vchiq_state *state) struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) && - (arm_state->vc_resume_state < VC_RESUME_REQUESTED) && - vchiq_videocore_wanted(state); + (arm_state->vc_resume_state < VC_RESUME_REQUESTED); } /* Initiate suspend via slot handler. Should be called with the write lock @@ -2736,17 +2719,10 @@ vchiq_release_internal(struct vchiq_state *state, struct vchiq_service *service) --arm_state->videocore_use_count; --(*entity_uc); - if (!vchiq_videocore_wanted(state)) { - vchiq_log_info(vchiq_susp_log_level, - "%s %s count %d, state count %d - suspending", - __func__, entity, *entity_uc, - arm_state->videocore_use_count); - vchiq_arm_vcsuspend(state); - } else - vchiq_log_trace(vchiq_susp_log_level, - "%s %s count %d, state count %d", - __func__, entity, *entity_uc, - arm_state->videocore_use_count); + vchiq_log_trace(vchiq_susp_log_level, + "%s %s count %d, state count %d", + __func__, entity, *entity_uc, + arm_state->videocore_use_count); unlock: write_unlock_bh(&arm_state->susp_res_lock); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 1874a01f5201..0a4efcf95a61 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -112,9 +112,6 @@ vchiq_release_service(unsigned int handle); extern enum vchiq_status vchiq_check_service(struct vchiq_service *service); -extern int -vchiq_platform_videocore_wanted(struct vchiq_state *state); - extern void vchiq_dump_platform_use_state(struct vchiq_state *state); @@ -124,8 +121,6 @@ vchiq_dump_service_use_state(struct vchiq_state *state); extern struct vchiq_arm_state* vchiq_platform_get_arm_state(struct vchiq_state *state); -extern int -vchiq_videocore_wanted(struct vchiq_state *state); extern enum vchiq_status vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, From 913e4e05a432e8b70fa8186f5aa308afc5776583 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:26 +0100 Subject: [PATCH 0029/2231] staging: vc04_services: Get rid of vchiq_platform_handle_timeout() The function does nothing. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-11-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_2835_arm.c | 5 ----- .../staging/vc04_services/interface/vchiq_arm/vchiq_core.c | 5 ----- .../staging/vc04_services/interface/vchiq_arm/vchiq_core.h | 3 --- 3 files changed, 13 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index a7f72dba2e20..c18c6ca0b6c0 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -257,11 +257,6 @@ int vchiq_dump_platform_state(void *dump_context) return vchiq_dump(dump_context, buf, len + 1); } -void -vchiq_platform_handle_timeout(struct vchiq_state *state) -{ - (void)state; -} /* * Local functions */ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index ef8340ab8a52..f135d55b29e5 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -1911,11 +1911,6 @@ slot_handler_func(void *v) "message"); } break; - - case VCHIQ_CONNSTATE_PAUSE_TIMEOUT: - case VCHIQ_CONNSTATE_RESUME_TIMEOUT: - vchiq_platform_handle_timeout(state); - break; default: break; } diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 535a67cc68ed..11037a499408 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -638,9 +638,6 @@ vchiq_platform_conn_state_changed(struct vchiq_state *state, enum vchiq_connstate oldstate, enum vchiq_connstate newstate); -extern void -vchiq_platform_handle_timeout(struct vchiq_state *state); - extern void vchiq_set_conn_state(struct vchiq_state *state, enum vchiq_connstate newstate); From c9b361ccd0e193d4628b376991830c3c42430506 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:27 +0100 Subject: [PATCH 0030/2231] staging: vc04_services: Get rid of vchiq_on_remote_use_active() Function does nothing. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-12-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 6 ------ .../staging/vc04_services/interface/vchiq_arm/vchiq_core.c | 1 - 2 files changed, 7 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index c147d94784eb..7c620366dfb6 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2962,12 +2962,6 @@ out: return ret; } -/* stub functions */ -void vchiq_on_remote_use_active(struct vchiq_state *state) -{ - (void)state; -} - void vchiq_platform_conn_state_changed(struct vchiq_state *state, enum vchiq_connstate oldstate, enum vchiq_connstate newstate) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index f135d55b29e5..c5493dee6dd7 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -1815,7 +1815,6 @@ parse_rx_slots(struct vchiq_state *state) vchiq_on_remote_release(state); break; case VCHIQ_MSG_REMOTE_USE_ACTIVE: - vchiq_on_remote_use_active(state); break; default: From af908f390fd181fe0f93cfea62ae72a3054d188f Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:28 +0100 Subject: [PATCH 0031/2231] staging: vc04_services: Get rid of vchiq_arm_vcsuspend() It's not used. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-13-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_arm.c | 51 ------------------- .../interface/vchiq_arm/vchiq_arm.h | 3 -- 2 files changed, 54 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 7c620366dfb6..a33f0fb5d45f 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2500,57 +2500,6 @@ need_resume(struct vchiq_state *state) (arm_state->vc_resume_state < VC_RESUME_REQUESTED); } -/* Initiate suspend via slot handler. Should be called with the write lock - * held */ -enum vchiq_status -vchiq_arm_vcsuspend(struct vchiq_state *state) -{ - enum vchiq_status status = VCHIQ_ERROR; - struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); - - if (!arm_state) - goto out; - - vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); - status = VCHIQ_SUCCESS; - - switch (arm_state->vc_suspend_state) { - case VC_SUSPEND_REQUESTED: - vchiq_log_info(vchiq_susp_log_level, "%s: suspend already " - "requested", __func__); - break; - case VC_SUSPEND_IN_PROGRESS: - vchiq_log_info(vchiq_susp_log_level, "%s: suspend already in " - "progress", __func__); - break; - - default: - /* We don't expect to be in other states, so log but continue - * anyway */ - vchiq_log_error(vchiq_susp_log_level, - "%s unexpected suspend state %s", __func__, - suspend_state_names[arm_state->vc_suspend_state + - VC_SUSPEND_NUM_OFFSET]); - /* fall through */ - case VC_SUSPEND_REJECTED: - case VC_SUSPEND_FAILED: - /* Ensure any idle state actions have been run */ - set_suspend_state(arm_state, VC_SUSPEND_IDLE); - /* fall through */ - case VC_SUSPEND_IDLE: - vchiq_log_info(vchiq_susp_log_level, - "%s: suspending", __func__); - set_suspend_state(arm_state, VC_SUSPEND_REQUESTED); - /* kick the slot handler thread to initiate suspend */ - request_poll(state, NULL, 0); - break; - } - -out: - vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); - return status; -} - void vchiq_platform_check_suspend(struct vchiq_state *state) { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 0a4efcf95a61..606a519dd3db 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -88,9 +88,6 @@ int vchiq_platform_init(struct platform_device *pdev, extern struct vchiq_state * vchiq_get_state(void); -extern enum vchiq_status -vchiq_arm_vcsuspend(struct vchiq_state *state); - extern enum vchiq_status vchiq_arm_vcresume(struct vchiq_state *state); From 6d044eac92e6a4f782391cb7083a8cc71279009c Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:29 +0100 Subject: [PATCH 0032/2231] staging: vc04_services: Get rid of vchiq_check_resume() Nobody calls this function. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-14-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_arm.c | 23 ------------------- .../interface/vchiq_arm/vchiq_arm.h | 3 --- 2 files changed, 26 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index a33f0fb5d45f..a26f9f6311a6 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2521,29 +2521,6 @@ out: return; } -/* This function should be called with the write lock held */ -int -vchiq_check_resume(struct vchiq_state *state) -{ - struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); - int resume = 0; - - if (!arm_state) - goto out; - - vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); - - if (need_resume(state)) { - set_resume_state(arm_state, VC_RESUME_REQUESTED); - request_poll(state, NULL, 0); - resume = 1; - } - -out: - vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); - return resume; -} - enum vchiq_status vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, enum USE_TYPE_E use_type) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 606a519dd3db..676b1cb6f332 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -95,9 +95,6 @@ extern enum vchiq_status vchiq_arm_init_state(struct vchiq_state *state, struct vchiq_arm_state *arm_state); -extern int -vchiq_check_resume(struct vchiq_state *state); - extern void vchiq_check_suspend(struct vchiq_state *state); enum vchiq_status From 26a6597506668781a8a7371bf8e5415814232dfb Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:30 +0100 Subject: [PATCH 0033/2231] staging: vc04_services: Delete vc_suspend_complete completion Nobody is waiting on it. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-15-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 7 ------- .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.h | 1 - 2 files changed, 8 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index a26f9f6311a6..f66280663d0d 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2356,8 +2356,6 @@ vchiq_arm_init_state(struct vchiq_state *state, atomic_set(&arm_state->ka_use_ack_count, 0); atomic_set(&arm_state->ka_release_count, 0); - init_completion(&arm_state->vc_suspend_complete); - init_completion(&arm_state->vc_resume_complete); /* Initialise to 'done' state. We only want to block on resume * completion while videocore is suspended. */ @@ -2436,18 +2434,14 @@ set_suspend_state(struct vchiq_arm_state *arm_state, /* state specific additional actions */ switch (new_state) { case VC_SUSPEND_FORCE_CANCELED: - complete_all(&arm_state->vc_suspend_complete); break; case VC_SUSPEND_REJECTED: - complete_all(&arm_state->vc_suspend_complete); break; case VC_SUSPEND_FAILED: - complete_all(&arm_state->vc_suspend_complete); arm_state->vc_resume_state = VC_RESUME_RESUMED; complete_all(&arm_state->vc_resume_complete); break; case VC_SUSPEND_IDLE: - reinit_completion(&arm_state->vc_suspend_complete); break; case VC_SUSPEND_REQUESTED: break; @@ -2455,7 +2449,6 @@ set_suspend_state(struct vchiq_arm_state *arm_state, set_resume_state(arm_state, VC_RESUME_IDLE); break; case VC_SUSPEND_SUSPENDED: - complete_all(&arm_state->vc_suspend_complete); break; default: BUG(); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 676b1cb6f332..aa081d642818 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -46,7 +46,6 @@ struct vchiq_arm_state { atomic_t ka_use_ack_count; atomic_t ka_release_count; - struct completion vc_suspend_complete; struct completion vc_resume_complete; rwlock_t susp_res_lock; From 6547980973b127c3604e61690870664b069e8f10 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:31 +0100 Subject: [PATCH 0034/2231] staging: vc04_services: Get rid of unused suspend/resume states It's impossible to get into them, so simply delete them. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-16-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_arm.c | 55 ------------------- .../interface/vchiq_arm/vchiq_arm.h | 7 --- 2 files changed, 62 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index f66280663d0d..db7ae204f50f 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -50,20 +50,13 @@ int vchiq_susp_log_level = VCHIQ_LOG_ERROR; #define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */ static const char *const suspend_state_names[] = { - "VC_SUSPEND_FORCE_CANCELED", - "VC_SUSPEND_REJECTED", - "VC_SUSPEND_FAILED", "VC_SUSPEND_IDLE", - "VC_SUSPEND_REQUESTED", "VC_SUSPEND_IN_PROGRESS", - "VC_SUSPEND_SUSPENDED" }; #define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */ static const char *const resume_state_names[] = { - "VC_RESUME_FAILED", "VC_RESUME_IDLE", "VC_RESUME_REQUESTED", - "VC_RESUME_IN_PROGRESS", "VC_RESUME_RESUMED" }; /* The number of times we allow force suspend to timeout before actually @@ -2396,18 +2389,6 @@ vchiq_arm_init_state(struct vchiq_state *state, ** before resuming can occur. We therefore also reset the ** resume state machine to VC_RESUME_IDLE in this state. ** -** VC_SUSPEND_SUSPENDED - Suspend has completed successfully. Also call -** complete_all on the suspend completion to notify -** anything waiting for suspend to happen. -** -** VC_SUSPEND_REJECTED - Videocore rejected suspend. Videocore will also -** initiate resume, so no need to alter resume state. -** We call complete_all on the suspend completion to notify -** of suspend rejection. -** -** VC_SUSPEND_FAILED - We failed to initiate videocore suspend. We notify the -** suspend completion and reset the resume state machine. -** ** VC_RESUME_IDLE - Initialise the resume completion at the same time. The ** resume completion is in it's 'done' state whenever ** videcore is running. Therefore, the VC_RESUME_IDLE @@ -2415,13 +2396,6 @@ vchiq_arm_init_state(struct vchiq_state *state, ** Hence, any thread which needs to wait until videocore is ** running can wait on this completion - it will only block ** if videocore is suspended. -** -** VC_RESUME_RESUMED - Resume has completed successfully. Videocore is running. -** Call complete_all on the resume completion to unblock -** any threads waiting for resume. Also reset the suspend -** state machine to it's idle state. -** -** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. */ void @@ -2433,23 +2407,11 @@ set_suspend_state(struct vchiq_arm_state *arm_state, /* state specific additional actions */ switch (new_state) { - case VC_SUSPEND_FORCE_CANCELED: - break; - case VC_SUSPEND_REJECTED: - break; - case VC_SUSPEND_FAILED: - arm_state->vc_resume_state = VC_RESUME_RESUMED; - complete_all(&arm_state->vc_resume_complete); - break; case VC_SUSPEND_IDLE: break; - case VC_SUSPEND_REQUESTED: - break; case VC_SUSPEND_IN_PROGRESS: set_resume_state(arm_state, VC_RESUME_IDLE); break; - case VC_SUSPEND_SUSPENDED: - break; default: BUG(); break; @@ -2465,15 +2427,11 @@ set_resume_state(struct vchiq_arm_state *arm_state, /* state specific additional actions */ switch (new_state) { - case VC_RESUME_FAILED: - break; case VC_RESUME_IDLE: reinit_completion(&arm_state->vc_resume_complete); break; case VC_RESUME_REQUESTED: break; - case VC_RESUME_IN_PROGRESS: - break; case VC_RESUME_RESUMED: complete_all(&arm_state->vc_resume_complete); set_suspend_state(arm_state, VC_SUSPEND_IDLE); @@ -2503,12 +2461,6 @@ vchiq_platform_check_suspend(struct vchiq_state *state) vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); - write_lock_bh(&arm_state->susp_res_lock); - if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED && - arm_state->vc_resume_state == VC_RESUME_RESUMED) - set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS); - write_unlock_bh(&arm_state->susp_res_lock); - out: vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); return; @@ -2548,13 +2500,6 @@ vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, local_uc = ++arm_state->videocore_use_count; local_entity_uc = ++(*entity_uc); - /* If there's a pending request which hasn't yet been serviced then - * just clear it. If we're past VC_SUSPEND_REQUESTED state then - * vc_resume_complete will block until we either resume or fail to - * suspend */ - if (arm_state->vc_suspend_state <= VC_SUSPEND_REQUESTED) - set_suspend_state(arm_state, VC_SUSPEND_IDLE); - if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) { set_resume_state(arm_state, VC_RESUME_REQUESTED); vchiq_log_info(vchiq_susp_log_level, diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index aa081d642818..afd822546e2e 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -15,20 +15,13 @@ #include "vchiq_debugfs.h" enum vc_suspend_status { - VC_SUSPEND_FORCE_CANCELED = -3, /* Force suspend canceled, too busy */ - VC_SUSPEND_REJECTED = -2, /* Videocore rejected suspend request */ - VC_SUSPEND_FAILED = -1, /* Videocore suspend failed */ VC_SUSPEND_IDLE = 0, /* VC active, no suspend actions */ - VC_SUSPEND_REQUESTED, /* User has requested suspend */ VC_SUSPEND_IN_PROGRESS, /* Slot handler has recvd suspend request */ - VC_SUSPEND_SUSPENDED /* Videocore suspend succeeded */ }; enum vc_resume_status { - VC_RESUME_FAILED = -1, /* Videocore resume failed */ VC_RESUME_IDLE = 0, /* VC suspended, no resume actions */ VC_RESUME_REQUESTED, /* User has requested resume */ - VC_RESUME_IN_PROGRESS, /* Slot handler has received resume request */ VC_RESUME_RESUMED /* Videocore resumed successfully (active) */ }; From bd8aa2850f001db441dfbd26ad7572cb99122f61 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:32 +0100 Subject: [PATCH 0035/2231] staging: vc04_services: Get of even more suspend/resume states After the first states cleanup pass some set_suspend/resume_state() calls disappeared which opened up even more opportunities for deletions. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-17-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_arm.c | 100 +----------------- .../interface/vchiq_arm/vchiq_arm.h | 12 --- 2 files changed, 5 insertions(+), 107 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index db7ae204f50f..cee643a33642 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -48,15 +48,8 @@ int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; int vchiq_susp_log_level = VCHIQ_LOG_ERROR; -#define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */ -static const char *const suspend_state_names[] = { - "VC_SUSPEND_IDLE", - "VC_SUSPEND_IN_PROGRESS", -}; #define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */ static const char *const resume_state_names[] = { - "VC_RESUME_IDLE", - "VC_RESUME_REQUESTED", "VC_RESUME_RESUMED" }; /* The number of times we allow force suspend to timeout before actually @@ -2361,63 +2354,6 @@ vchiq_arm_init_state(struct vchiq_state *state, return VCHIQ_SUCCESS; } -/* -** Functions to modify the state variables; -** set_suspend_state -** set_resume_state -** -** There are more state variables than we might like, so ensure they remain in -** step. Suspend and resume state are maintained separately, since most of -** these state machines can operate independently. However, there are a few -** states where state transitions in one state machine cause a reset to the -** other state machine. In addition, there are some completion events which -** need to occur on state machine reset and end-state(s), so these are also -** dealt with in these functions. -** -** In all states we set the state variable according to the input, but in some -** cases we perform additional steps outlined below; -** -** VC_SUSPEND_IDLE - Initialise the suspend completion at the same time. -** The suspend completion is completed after any suspend -** attempt. When we reset the state machine we also reset -** the completion. This reset occurs when videocore is -** resumed, and also if we initiate suspend after a suspend -** failure. -** -** VC_SUSPEND_IN_PROGRESS - This state is considered the point of no return for -** suspend - ie from this point on we must try to suspend -** before resuming can occur. We therefore also reset the -** resume state machine to VC_RESUME_IDLE in this state. -** -** VC_RESUME_IDLE - Initialise the resume completion at the same time. The -** resume completion is in it's 'done' state whenever -** videcore is running. Therefore, the VC_RESUME_IDLE -** state implies that videocore is suspended. -** Hence, any thread which needs to wait until videocore is -** running can wait on this completion - it will only block -** if videocore is suspended. -*/ - -void -set_suspend_state(struct vchiq_arm_state *arm_state, - enum vc_suspend_status new_state) -{ - /* set the state in all cases */ - arm_state->vc_suspend_state = new_state; - - /* state specific additional actions */ - switch (new_state) { - case VC_SUSPEND_IDLE: - break; - case VC_SUSPEND_IN_PROGRESS: - set_resume_state(arm_state, VC_RESUME_IDLE); - break; - default: - BUG(); - break; - } -} - void set_resume_state(struct vchiq_arm_state *arm_state, enum vc_resume_status new_state) @@ -2427,14 +2363,8 @@ set_resume_state(struct vchiq_arm_state *arm_state, /* state specific additional actions */ switch (new_state) { - case VC_RESUME_IDLE: - reinit_completion(&arm_state->vc_resume_complete); - break; - case VC_RESUME_REQUESTED: - break; case VC_RESUME_RESUMED: complete_all(&arm_state->vc_resume_complete); - set_suspend_state(arm_state, VC_SUSPEND_IDLE); break; default: BUG(); @@ -2442,15 +2372,6 @@ set_resume_state(struct vchiq_arm_state *arm_state, } } -static inline int -need_resume(struct vchiq_state *state) -{ - struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); - - return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) && - (arm_state->vc_resume_state < VC_RESUME_REQUESTED); -} - void vchiq_platform_check_suspend(struct vchiq_state *state) { @@ -2500,16 +2421,9 @@ vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, local_uc = ++arm_state->videocore_use_count; local_entity_uc = ++(*entity_uc); - if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) { - set_resume_state(arm_state, VC_RESUME_REQUESTED); - vchiq_log_info(vchiq_susp_log_level, - "%s %s count %d, state count %d", - __func__, entity, local_entity_uc, local_uc); - request_poll(state, NULL, 0); - } else - vchiq_log_trace(vchiq_susp_log_level, - "%s %s count %d, state count %d", - __func__, entity, *entity_uc, local_uc); + vchiq_log_trace(vchiq_susp_log_level, + "%s %s count %d, state count %d", + __func__, entity, *entity_uc, local_uc); write_unlock_bh(&arm_state->susp_res_lock); @@ -2720,7 +2634,6 @@ vchiq_dump_service_use_state(struct vchiq_state *state) int only_nonzero = 0; static const char *nz = "<-- preventing suspend"; - enum vc_suspend_status vc_suspend_state; enum vc_resume_status vc_resume_state; int peer_count; int vc_use_count; @@ -2735,7 +2648,6 @@ vchiq_dump_service_use_state(struct vchiq_state *state) return; read_lock_bh(&arm_state->susp_res_lock); - vc_suspend_state = arm_state->vc_suspend_state; vc_resume_state = arm_state->vc_resume_state; peer_count = arm_state->peer_use_count; vc_use_count = arm_state->videocore_use_count; @@ -2814,12 +2726,10 @@ vchiq_check_service(struct vchiq_service *service) if (ret == VCHIQ_ERROR) { vchiq_log_error(vchiq_susp_log_level, "%s ERROR - %c%c%c%c:%d service count %d, " - "state count %d, videocore suspend state %s", __func__, + "state count %d", __func__, VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), service->client_id, service->service_use_count, - arm_state->videocore_use_count, - suspend_state_names[arm_state->vc_suspend_state + - VC_SUSPEND_NUM_OFFSET]); + arm_state->videocore_use_count); vchiq_dump_service_use_state(service->state); } out: diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index afd822546e2e..b1fad282db04 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -14,14 +14,7 @@ #include "vchiq_core.h" #include "vchiq_debugfs.h" -enum vc_suspend_status { - VC_SUSPEND_IDLE = 0, /* VC active, no suspend actions */ - VC_SUSPEND_IN_PROGRESS, /* Slot handler has recvd suspend request */ -}; - enum vc_resume_status { - VC_RESUME_IDLE = 0, /* VC suspended, no resume actions */ - VC_RESUME_REQUESTED, /* User has requested resume */ VC_RESUME_RESUMED /* Videocore resumed successfully (active) */ }; @@ -42,7 +35,6 @@ struct vchiq_arm_state { struct completion vc_resume_complete; rwlock_t susp_res_lock; - enum vc_suspend_status vc_suspend_state; enum vc_resume_status vc_resume_state; struct vchiq_state *state; @@ -130,10 +122,6 @@ vchiq_instance_get_trace(struct vchiq_instance *instance); extern void vchiq_instance_set_trace(struct vchiq_instance *instance, int trace); -extern void -set_suspend_state(struct vchiq_arm_state *arm_state, - enum vc_suspend_status new_state); - extern void set_resume_state(struct vchiq_arm_state *arm_state, enum vc_resume_status new_state); From ed151829e3539b9ae511cc28eb496c383d3e24cb Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:33 +0100 Subject: [PATCH 0036/2231] staging: vc04_services: Get rid of the rest of suspend/resume state handling The only remaining state doesn't provide any information anymore nor the completion that depends on it, which is set to complete_all() before any user may wait on it. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-18-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_arm.c | 52 ------------------- .../interface/vchiq_arm/vchiq_arm.h | 11 ---- 2 files changed, 63 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index cee643a33642..e753ccffd588 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -48,10 +48,6 @@ int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; int vchiq_susp_log_level = VCHIQ_LOG_ERROR; -#define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */ -static const char *const resume_state_names[] = { - "VC_RESUME_RESUMED" -}; /* The number of times we allow force suspend to timeout before actually ** _forcing_ suspend. This is to cater for SW which fails to release vchiq ** correctly - we don't want to prevent ARM suspend indefinitely in this case. @@ -2342,11 +2338,6 @@ vchiq_arm_init_state(struct vchiq_state *state, atomic_set(&arm_state->ka_use_ack_count, 0); atomic_set(&arm_state->ka_release_count, 0); - init_completion(&arm_state->vc_resume_complete); - /* Initialise to 'done' state. We only want to block on resume - * completion while videocore is suspended. */ - set_resume_state(arm_state, VC_RESUME_RESUMED); - arm_state->state = state; arm_state->first_connect = 0; @@ -2354,24 +2345,6 @@ vchiq_arm_init_state(struct vchiq_state *state, return VCHIQ_SUCCESS; } -void -set_resume_state(struct vchiq_arm_state *arm_state, - enum vc_resume_status new_state) -{ - /* set the state in all cases */ - arm_state->vc_resume_state = new_state; - - /* state specific additional actions */ - switch (new_state) { - case VC_RESUME_RESUMED: - complete_all(&arm_state->vc_resume_complete); - break; - default: - BUG(); - break; - } -} - void vchiq_platform_check_suspend(struct vchiq_state *state) { @@ -2427,22 +2400,6 @@ vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, write_unlock_bh(&arm_state->susp_res_lock); - /* Completion is in a done state when we're not suspended, so this won't - * block for the non-suspended case. */ - if (!try_wait_for_completion(&arm_state->vc_resume_complete)) { - vchiq_log_info(vchiq_susp_log_level, "%s %s wait for resume", - __func__, entity); - if (wait_for_completion_killable( - &arm_state->vc_resume_complete)) { - vchiq_log_error(vchiq_susp_log_level, "%s %s wait for " - "resume interrupted", __func__, entity); - ret = VCHIQ_ERROR; - goto out; - } - vchiq_log_info(vchiq_susp_log_level, "%s %s resumed", __func__, - entity); - } - if (ret == VCHIQ_SUCCESS) { enum vchiq_status status = VCHIQ_SUCCESS; long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0); @@ -2634,7 +2591,6 @@ vchiq_dump_service_use_state(struct vchiq_state *state) int only_nonzero = 0; static const char *nz = "<-- preventing suspend"; - enum vc_resume_status vc_resume_state; int peer_count; int vc_use_count; int active_services; @@ -2648,7 +2604,6 @@ vchiq_dump_service_use_state(struct vchiq_state *state) return; read_lock_bh(&arm_state->susp_res_lock); - vc_resume_state = arm_state->vc_resume_state; peer_count = arm_state->peer_use_count; vc_use_count = arm_state->videocore_use_count; active_services = state->unused_service; @@ -2677,13 +2632,6 @@ vchiq_dump_service_use_state(struct vchiq_state *state) read_unlock_bh(&arm_state->susp_res_lock); - vchiq_log_warning(vchiq_susp_log_level, - "-- Videcore suspend state: %s --", - suspend_state_names[vc_suspend_state + VC_SUSPEND_NUM_OFFSET]); - vchiq_log_warning(vchiq_susp_log_level, - "-- Videcore resume state: %s --", - resume_state_names[vc_resume_state + VC_RESUME_NUM_OFFSET]); - if (only_nonzero) vchiq_log_warning(vchiq_susp_log_level, "Too many active " "services (%d). Only dumping up to first %d services " diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index b1fad282db04..54c8796d5004 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -14,10 +14,6 @@ #include "vchiq_core.h" #include "vchiq_debugfs.h" -enum vc_resume_status { - VC_RESUME_RESUMED /* Videocore resumed successfully (active) */ -}; - enum USE_TYPE_E { USE_TYPE_SERVICE, USE_TYPE_SERVICE_NO_RESUME, @@ -32,10 +28,7 @@ struct vchiq_arm_state { atomic_t ka_use_ack_count; atomic_t ka_release_count; - struct completion vc_resume_complete; - rwlock_t susp_res_lock; - enum vc_resume_status vc_resume_state; struct vchiq_state *state; @@ -122,8 +115,4 @@ vchiq_instance_get_trace(struct vchiq_instance *instance); extern void vchiq_instance_set_trace(struct vchiq_instance *instance, int trace); -extern void -set_resume_state(struct vchiq_arm_state *arm_state, - enum vc_resume_status new_state); - #endif /* VCHIQ_ARM_H */ From b2a86ecd8f06dc44cecfe1ec897fb6929afcc0ee Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:34 +0100 Subject: [PATCH 0037/2231] staging: vc04_services: Get rid of USE_TYPE_SERVICE_NO_RESUME It's not used anywhere. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-19-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 54c8796d5004..795fd82a1d00 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -16,7 +16,6 @@ enum USE_TYPE_E { USE_TYPE_SERVICE, - USE_TYPE_SERVICE_NO_RESUME, USE_TYPE_VCHIQ }; From aca055ed40ea8adfa5ac0c99dbb380ec05c15b5b Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:35 +0100 Subject: [PATCH 0038/2231] staging: vc04_services: Delete vchiq_platform_check_suspend() The function does nothing. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-20-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 15 --------------- .../interface/vchiq_arm/vchiq_core.c | 3 --- .../interface/vchiq_arm/vchiq_core.h | 3 --- 3 files changed, 21 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index e753ccffd588..81b1c98cae3c 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2345,21 +2345,6 @@ vchiq_arm_init_state(struct vchiq_state *state, return VCHIQ_SUCCESS; } -void -vchiq_platform_check_suspend(struct vchiq_state *state) -{ - struct vchiq_arm_state *arm_state = vchiq_platform_get_arm_state(state); - - if (!arm_state) - goto out; - - vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); - -out: - vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); - return; -} - enum vchiq_status vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service, enum USE_TYPE_E use_type) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index c5493dee6dd7..d5957411d906 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -1866,9 +1866,6 @@ slot_handler_func(void *v) DEBUG_TRACE(SLOT_HANDLER_LINE); if (state->poll_needed) { - /* Check if we need to suspend - may change our - * conn_state */ - vchiq_platform_check_suspend(state); state->poll_needed = 0; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 11037a499408..604d0c330819 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -590,9 +590,6 @@ vchiq_complete_bulk(struct vchiq_bulk *bulk); extern void remote_event_signal(struct remote_event *event); -void -vchiq_platform_check_suspend(struct vchiq_state *state); - extern int vchiq_dump(void *dump_context, const char *str, int len); From a0184abeec6669aea64fda6975cef3f73a813946 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:36 +0100 Subject: [PATCH 0039/2231] staging: vc04_services: Get rid of vchiq_arm_vcresume()'s signature The function implementation does not exist. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-21-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 795fd82a1d00..0784c5002417 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -64,9 +64,6 @@ int vchiq_platform_init(struct platform_device *pdev, extern struct vchiq_state * vchiq_get_state(void); -extern enum vchiq_status -vchiq_arm_vcresume(struct vchiq_state *state); - extern enum vchiq_status vchiq_arm_init_state(struct vchiq_state *state, struct vchiq_arm_state *arm_state); From 12396526807d2ce56ef9cb2f9245c0a71fecf25f Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 31 Jan 2020 11:38:37 +0100 Subject: [PATCH 0040/2231] staging: vc04_services: vchiq_arm: Get rid of unused defines They aren't used anywhere. Get rid of them. Signed-off-by: Nicolas Saenz Julienne Link: https://lore.kernel.org/r/20200131103836.14312-22-nsaenzjulienne@suse.de Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 81b1c98cae3c..c456ced431af 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -48,16 +48,6 @@ int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; int vchiq_susp_log_level = VCHIQ_LOG_ERROR; -/* The number of times we allow force suspend to timeout before actually -** _forcing_ suspend. This is to cater for SW which fails to release vchiq -** correctly - we don't want to prevent ARM suspend indefinitely in this case. -*/ -#define FORCE_SUSPEND_FAIL_MAX 8 - -/* The time in ms allowed for videocore to go idle when force suspend has been - * requested */ -#define FORCE_SUSPEND_TIMEOUT_MS 200 - struct user_service { struct vchiq_service *service; void *userdata; From 226f912a0f7a85134dc457c54c2a806140e4e50a Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Thu, 6 Feb 2020 09:59:24 +0100 Subject: [PATCH 0041/2231] staging: rtl8188eu: remove unnecessary RETURN label Remove unnecessary RETURN label and use return directly instead. Since the return type of rtw_free_netdev() is void, remove the return at the end of the function. Signed-off-by: Michael Straube Link: https://lore.kernel.org/r/20200206085924.21531-1-straube.linux@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8188eu/os_dep/osdep_service.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/staging/rtl8188eu/os_dep/osdep_service.c b/drivers/staging/rtl8188eu/os_dep/osdep_service.c index 69d4b1d66b6f..4ba2378a1bb8 100644 --- a/drivers/staging/rtl8188eu/os_dep/osdep_service.c +++ b/drivers/staging/rtl8188eu/os_dep/osdep_service.c @@ -31,12 +31,11 @@ struct net_device *rtw_alloc_etherdev_with_old_priv(void *old_priv) pnetdev = alloc_etherdev_mq(sizeof(struct rtw_netdev_priv_indicator), 4); if (!pnetdev) - goto RETURN; + return NULL; pnpi = netdev_priv(pnetdev); pnpi->priv = old_priv; -RETURN: return pnetdev; } @@ -45,18 +44,15 @@ void rtw_free_netdev(struct net_device *netdev) struct rtw_netdev_priv_indicator *pnpi; if (!netdev) - goto RETURN; + return; pnpi = netdev_priv(netdev); if (!pnpi->priv) - goto RETURN; + return; vfree(pnpi->priv); free_netdev(netdev); - -RETURN: - return; } void rtw_buf_free(u8 **buf, u32 *buf_len) From be5418d45ba43407249b29c35926ebb78425a93a Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Sat, 8 Feb 2020 14:06:25 +0530 Subject: [PATCH 0042/2231] staging: pi433: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header files related to pi433 radio module drivers. It assigns explicit block comment to the SPDX License Identifier. Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Link: https://lore.kernel.org/r/20200208083621.GA3797@nishad Signed-off-by: Greg Kroah-Hartman --- drivers/staging/pi433/pi433_if.h | 4 ++-- drivers/staging/pi433/rf69.h | 4 ++-- drivers/staging/pi433/rf69_enum.h | 4 ++-- drivers/staging/pi433/rf69_registers.h | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/pi433/pi433_if.h b/drivers/staging/pi433/pi433_if.h index 9feb95c431cb..16c5b7fba249 100644 --- a/drivers/staging/pi433/pi433_if.h +++ b/drivers/staging/pi433/pi433_if.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0+ - * +/* SPDX-License-Identifier: GPL-2.0+ */ +/* * include/linux/TODO * * userspace interface for pi433 radio module diff --git a/drivers/staging/pi433/rf69.h b/drivers/staging/pi433/rf69.h index d43a8d87d5d3..b648ba5fff89 100644 --- a/drivers/staging/pi433/rf69.h +++ b/drivers/staging/pi433/rf69.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0+ - * +/* SPDX-License-Identifier: GPL-2.0+ */ +/* * hardware abstraction/register access for HopeRf rf69 radio module * * Copyright (C) 2016 Wolf-Entwicklungen diff --git a/drivers/staging/pi433/rf69_enum.h b/drivers/staging/pi433/rf69_enum.h index 3ee1952245c2..fbf56fcf5fe8 100644 --- a/drivers/staging/pi433/rf69_enum.h +++ b/drivers/staging/pi433/rf69_enum.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0+ - * +/* SPDX-License-Identifier: GPL-2.0+ */ +/* * enumerations for HopeRf rf69 radio module * * Copyright (C) 2016 Wolf-Entwicklungen diff --git a/drivers/staging/pi433/rf69_registers.h b/drivers/staging/pi433/rf69_registers.h index be5497cdace0..a170c66c3d5b 100644 --- a/drivers/staging/pi433/rf69_registers.h +++ b/drivers/staging/pi433/rf69_registers.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0+ - * +/* SPDX-License-Identifier: GPL-2.0+ */ +/* * register description for HopeRf rf69 radio module * * Copyright (C) 2016 Wolf-Entwicklungen From 4ed6aba987bc573c0b70b952522843c511080c3c Mon Sep 17 00:00:00 2001 From: Sumera Priyadarsini Date: Sun, 9 Feb 2020 13:00:20 +0530 Subject: [PATCH 0043/2231] staging: rtl8192u: Add space to fix style issue This patch fixes the file r8190_rtl8256.c to avoid the following checkpatch.pl warnings: CHECK: spaces preferred around that '>>' (ctx:VxV) Signed-off-by: Sumera Priyadarsini Link: https://lore.kernel.org/r/20200209073020.13423-1-sylphrenadin@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8192u/r819xU_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8192u/r819xU_phy.c b/drivers/staging/rtl8192u/r819xU_phy.c index 555e52522be6..37b99cf4b35f 100644 --- a/drivers/staging/rtl8192u/r819xU_phy.c +++ b/drivers/staging/rtl8192u/r819xU_phy.c @@ -1531,7 +1531,7 @@ void rtl8192_SetBWModeWorkItem(struct net_device *dev) rtl8192_setBBreg(dev, rFPGA0_RFMOD, bRFMOD, 0x1); rtl8192_setBBreg(dev, rFPGA1_RFMOD, bRFMOD, 0x1); rtl8192_setBBreg(dev, rCCK0_System, bCCKSideBand, - priv->nCur40MhzPrimeSC>>1); + priv->nCur40MhzPrimeSC >> 1); rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x00100000, 0); rtl8192_setBBreg(dev, rOFDM1_LSTF, 0xC00, priv->nCur40MhzPrimeSC); From 0e3ee7f10af9b3c6994c530048d5115d3c67c801 Mon Sep 17 00:00:00 2001 From: Mohana Datta Yelugoti Date: Sun, 9 Feb 2020 23:06:28 +0530 Subject: [PATCH 0044/2231] staging: qlge: remove spaces at the start of a line This patch fixes "WARNING: please, no spaces at the start of a line" by checkpatch.pl by replacing spaces with the tab. Signed-off-by: Mohana Datta Yelugoti Link: https://lore.kernel.org/r/20200209173628.21221-1-ymdatta.work@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/qlge/qlge_main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/qlge/qlge_main.c b/drivers/staging/qlge/qlge_main.c index ef8037d0b52e..86b9b7314a40 100644 --- a/drivers/staging/qlge/qlge_main.c +++ b/drivers/staging/qlge/qlge_main.c @@ -52,16 +52,16 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); static const u32 default_msg = - NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | + NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | /* NETIF_MSG_TIMER | */ - NETIF_MSG_IFDOWN | - NETIF_MSG_IFUP | - NETIF_MSG_RX_ERR | - NETIF_MSG_TX_ERR | + NETIF_MSG_IFDOWN | + NETIF_MSG_IFUP | + NETIF_MSG_RX_ERR | + NETIF_MSG_TX_ERR | /* NETIF_MSG_TX_QUEUED | */ /* NETIF_MSG_INTR | NETIF_MSG_TX_DONE | NETIF_MSG_RX_STATUS | */ /* NETIF_MSG_PKTDATA | */ - NETIF_MSG_HW | NETIF_MSG_WOL | 0; + NETIF_MSG_HW | NETIF_MSG_WOL | 0; static int debug = -1; /* defaults above */ module_param(debug, int, 0664); From 32553441569482e36e65371edb84494bcec53c03 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Jan 2020 11:06:58 +0100 Subject: [PATCH 0045/2231] USB: serial: relax unthrottle memory barrier Commit a8d78d9f3856 ("USB: serial: clean up throttle handling") converted the throttle handling to use atomic bitops. This means that we can relax the smp_mb() in unthrottle() to smp_mb__after_atomic(), which for example is a no-op on architectures like x86 that provide fully ordered atomics. Reviewed-by: Greg Kroah-Hartman Reviewed-by: Davidlohr Bueso Signed-off-by: Johan Hovold --- drivers/usb/serial/generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 1be8bea372a2..546a1c2ce2f2 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -417,7 +417,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) /* * Make sure URB is marked as free before checking the throttled flag * to avoid racing with unthrottle() on another CPU. Matches the - * smp_mb() in unthrottle(). + * smp_mb__after_atomic() in unthrottle(). */ smp_mb__after_atomic(); @@ -489,7 +489,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) * Matches the smp_mb__after_atomic() in * usb_serial_generic_read_bulk_callback(). */ - smp_mb(); + smp_mb__after_atomic(); usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); } From 2c0bee081315b18064fe39661e679b2fe6b86476 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 8 Feb 2020 16:46:01 +0000 Subject: [PATCH 0046/2231] USB: serial: digi_acceleport: remove redundant assignment to pointer priv Pointer priv is being assigned with a value that is never read, it is assigned a new value later on in a for-loop. The assignment is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 578ebdd86520..91055a191995 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1472,7 +1472,7 @@ static int digi_read_oob_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial; struct tty_struct *tty; - struct digi_port *priv = usb_get_serial_port_data(port); + struct digi_port *priv; unsigned char *buf = urb->transfer_buffer; int opcode, line, status, val; unsigned long flags; From 075a329591540affcc32af0da09abd54549e1659 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 7 Feb 2020 15:13:57 +0000 Subject: [PATCH 0047/2231] staging: comedi: ni_routes: Refactor ni_find_valid_routes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out the loops in `ni_find_valid_routes()` into separate functions: * ni_find_route_values(device_family) to find the list of route values for a device family (e.g "ni-mseries"); and * ni_find_valid_routes(board_name) to find the set of valid routes for a board name. The functions above return `NULL` if the information is not found (as we do not currently have the routing information available for all supported boards). Cc: Éric Piel Cc: Spencer E. Olson Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20200207151400.272678-2-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/ni_routes.c | 58 +++++++++++++++------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/drivers/staging/comedi/drivers/ni_routes.c b/drivers/staging/comedi/drivers/ni_routes.c index 8f398b30f5bf..508f76c5c574 100644 --- a/drivers/staging/comedi/drivers/ni_routes.c +++ b/drivers/staging/comedi/drivers/ni_routes.c @@ -49,6 +49,43 @@ /* Helper for accessing data. */ #define RVi(table, src, dest) ((table)[(dest) * NI_NUM_NAMES + (src)]) +/* + * Find the route values for a device family. + */ +static const u8 *ni_find_route_values(const char *device_family) +{ + const u8 *rv = NULL; + int i; + + for (i = 0; ni_all_route_values[i]; ++i) { + if (memcmp(ni_all_route_values[i]->family, device_family, + strnlen(device_family, 30)) == 0) { + rv = &ni_all_route_values[i]->register_values[0][0]; + break; + } + } + return rv; +} + +/* + * Find the valid routes for a board. + */ +static const struct ni_device_routes * +ni_find_valid_routes(const char *board_name) +{ + const struct ni_device_routes *dr = NULL; + int i; + + for (i = 0; ni_device_routes_list[i]; ++i) { + if (memcmp(ni_device_routes_list[i]->device, board_name, + strnlen(board_name, 30)) == 0) { + dr = ni_device_routes_list[i]; + break; + } + } + return dr; +} + /* * Find the proper route_values and ni_device_routes tables for this particular * device. @@ -59,27 +96,14 @@ static int ni_find_device_routes(const char *device_family, const char *board_name, struct ni_route_tables *tables) { - const struct ni_device_routes *dr = NULL; - const u8 *rv = NULL; - int i; + const struct ni_device_routes *dr; + const u8 *rv; /* First, find the register_values table for this device family */ - for (i = 0; ni_all_route_values[i]; ++i) { - if (memcmp(ni_all_route_values[i]->family, device_family, - strnlen(device_family, 30)) == 0) { - rv = &ni_all_route_values[i]->register_values[0][0]; - break; - } - } + rv = ni_find_route_values(device_family); /* Second, find the set of routes valid for this device. */ - for (i = 0; ni_device_routes_list[i]; ++i) { - if (memcmp(ni_device_routes_list[i]->device, board_name, - strnlen(board_name, 30)) == 0) { - dr = ni_device_routes_list[i]; - break; - } - } + dr = ni_find_valid_routes(board_name); tables->route_values = rv; tables->valid_routes = dr; From e3b7ce73c578b77b592bd3f2da71b90f2c581c87 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 7 Feb 2020 15:13:58 +0000 Subject: [PATCH 0048/2231] staging: comedi: ni_routes: Allow alternate board name for routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not have or provide routing information available for all supported boards. Some of the boards for which we do not currently provide routing information actually have identical routes to a similar board for which we do provide routing information. To avoid having to provide duplicate routing information, add an "alternate board name" parameter (possibly `NULl`) to `ni_assign_device_routes()` and `ni_find_device_routes()`. If the routing information cannot be found for the actual board name, try finding it using the alternate board name. Cc: Éric Piel Cc: Spencer E. Olson Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20200207151400.272678-3-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/ni_660x.c | 2 +- .../staging/comedi/drivers/ni_mio_common.c | 2 +- drivers/staging/comedi/drivers/ni_routes.c | 21 +++++++++++++++++-- drivers/staging/comedi/drivers/ni_routes.h | 1 + 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c index 4ee9b260eab0..75d5c9c24596 100644 --- a/drivers/staging/comedi/drivers/ni_660x.c +++ b/drivers/staging/comedi/drivers/ni_660x.c @@ -1035,7 +1035,7 @@ static int ni_660x_auto_attach(struct comedi_device *dev, ni_660x_init_tio_chips(dev, board->n_chips); /* prepare the device for globally-named routes. */ - if (ni_assign_device_routes("ni_660x", board->name, + if (ni_assign_device_routes("ni_660x", board->name, NULL, &devpriv->routing_tables) < 0) { dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n", __func__, board->name); diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index f98e3ae27bff..a1578868ee96 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -5974,7 +5974,7 @@ static int ni_E_init(struct comedi_device *dev, : "ni_eseries"; /* prepare the device for globally-named routes. */ - if (ni_assign_device_routes(dev_family, board->name, + if (ni_assign_device_routes(dev_family, board->name, NULL, &devpriv->routing_tables) < 0) { dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n", __func__, board->name); diff --git a/drivers/staging/comedi/drivers/ni_routes.c b/drivers/staging/comedi/drivers/ni_routes.c index 508f76c5c574..07cb970340db 100644 --- a/drivers/staging/comedi/drivers/ni_routes.c +++ b/drivers/staging/comedi/drivers/ni_routes.c @@ -88,12 +88,14 @@ ni_find_valid_routes(const char *board_name) /* * Find the proper route_values and ni_device_routes tables for this particular - * device. + * device. Possibly try an alternate board name if device routes not found + * for the actual board name. * * Return: -ENODATA if either was not found; 0 if both were found. */ static int ni_find_device_routes(const char *device_family, const char *board_name, + const char *alt_board_name, struct ni_route_tables *tables) { const struct ni_device_routes *dr; @@ -104,6 +106,8 @@ static int ni_find_device_routes(const char *device_family, /* Second, find the set of routes valid for this device. */ dr = ni_find_valid_routes(board_name); + if (!dr && alt_board_name) + dr = ni_find_valid_routes(alt_board_name); tables->route_values = rv; tables->valid_routes = dr; @@ -117,15 +121,28 @@ static int ni_find_device_routes(const char *device_family, /** * ni_assign_device_routes() - Assign the proper lookup table for NI signal * routing to the specified NI device. + * @device_family: Device family name (determines route values). + * @board_name: Board name (determines set of routes). + * @alt_board_name: Optional alternate board name to try on failure. + * @tables: Pointer to assigned routing information. + * + * Finds the route values for the device family and the set of valid routes + * for the board. If valid routes could not be found for the actual board + * name and an alternate board name has been specified, try that one. + * + * On failure, the assigned routing information may be partially filled + * (for example, with the route values but not the set of valid routes). * * Return: -ENODATA if assignment was not successful; 0 if successful. */ int ni_assign_device_routes(const char *device_family, const char *board_name, + const char *alt_board_name, struct ni_route_tables *tables) { memset(tables, 0, sizeof(struct ni_route_tables)); - return ni_find_device_routes(device_family, board_name, tables); + return ni_find_device_routes(device_family, board_name, alt_board_name, + tables); } EXPORT_SYMBOL_GPL(ni_assign_device_routes); diff --git a/drivers/staging/comedi/drivers/ni_routes.h b/drivers/staging/comedi/drivers/ni_routes.h index 3211a16adc6f..b7680fd2afe1 100644 --- a/drivers/staging/comedi/drivers/ni_routes.h +++ b/drivers/staging/comedi/drivers/ni_routes.h @@ -76,6 +76,7 @@ struct ni_route_tables { */ int ni_assign_device_routes(const char *device_family, const char *board_name, + const char *alt_board_name, struct ni_route_tables *tables); /* From 9bfd8bc5e8bfabdc20eb9b528192f018498cf3db Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 7 Feb 2020 15:13:59 +0000 Subject: [PATCH 0049/2231] staging: comedi: ni_mio_common: Allow alternate board name for routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not have or do not provide routing information for all supported boards. Some of the boards for which we do not provide routing information actually have routes that are identical to a similar board for which we already provide routing information. To allow boards to share identical routing information, add an `alt_route_name` member to `struct ni_board_struct`. This will be initialized to `NULL` for all boards except those that will use make use of the identical routing information that has been provided for a similar board, in which case it will name that board. Pass the `alt_route_name` member value to `ni_assign_device_routes()` as the `alt_board_name` parameter, which it will use if no routing information could be found for the actual board name. Cc: Éric Piel Cc: Spencer E. Olson Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20200207151400.272678-4-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/ni_mio_common.c | 3 ++- drivers/staging/comedi/drivers/ni_stc.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index a1578868ee96..b72a40a79930 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -5974,7 +5974,8 @@ static int ni_E_init(struct comedi_device *dev, : "ni_eseries"; /* prepare the device for globally-named routes. */ - if (ni_assign_device_routes(dev_family, board->name, NULL, + if (ni_assign_device_routes(dev_family, board->name, + board->alt_route_name, &devpriv->routing_tables) < 0) { dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n", __func__, board->name); diff --git a/drivers/staging/comedi/drivers/ni_stc.h b/drivers/staging/comedi/drivers/ni_stc.h index 35427f8bf8f7..fbc0b753a0f5 100644 --- a/drivers/staging/comedi/drivers/ni_stc.h +++ b/drivers/staging/comedi/drivers/ni_stc.h @@ -941,6 +941,7 @@ enum ni_reg_type { struct ni_board_struct { const char *name; + const char *alt_route_name; int device_id; int isapnp_id; From 26a99c06b6bbef18c52f1159598c3f56cc03aaef Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 7 Feb 2020 15:14:00 +0000 Subject: [PATCH 0050/2231] staging: comedi: ni_pcimio: add routes for NI PCIe-6251 and PCIe-6259 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not currently provide routing information for NI PCIe-6251 and PCI-6259 boards, but they are functionally identical to the PCI-6251 and PCI-6259 boards and can share their routing information. (This has been confirmed for the PCIe-6251 by Éric Piel, using the "NI MAX" software for Windows. It is hoped that it applies to PCIe-6259, but has not yet been checked due to lack of hardware.) Initialize the `alt_route_name` member of the board information for PCIe-6251 and PCIe-6259 to allow them to make use of the routing information provided for PCI-6251 and PCI-6259 respectively. Cc: Éric Piel Cc: Spencer E. Olson Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20200207151400.272678-5-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/ni_pcimio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c index 14b26fffe049..7c82d5f9778f 100644 --- a/drivers/staging/comedi/drivers/ni_pcimio.c +++ b/drivers/staging/comedi/drivers/ni_pcimio.c @@ -888,6 +888,7 @@ static const struct ni_board_struct ni_boards[] = { }, [BOARD_PCIE6251] = { .name = "pcie-6251", + .alt_route_name = "pci-6251", .n_adchan = 16, .ai_maxdata = 0xffff, .ai_fifo_depth = 4095, @@ -976,6 +977,7 @@ static const struct ni_board_struct ni_boards[] = { }, [BOARD_PCIE6259] = { .name = "pcie-6259", + .alt_route_name = "pci-6259", .n_adchan = 32, .ai_maxdata = 0xffff, .ai_fifo_depth = 4095, From bf2b8fe0d7d2810a0cbd28c5dfa12f90a051c61d Mon Sep 17 00:00:00 2001 From: JieunKim Date: Mon, 10 Feb 2020 18:14:20 +0900 Subject: [PATCH 0051/2231] staging: exfat: Replace printk with pr_info pr_info is preferred to use than printk. pr_info calls printk with KERN_INFO macros by itself. Signed-off-by: JieunKim Link: https://lore.kernel.org/r/20200210091421.12335-1-jieun.kim4758@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat_super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 1127131ee043..7abe76b9237b 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -364,7 +364,7 @@ static int ffsMountVol(struct super_block *sb) exfat_bdev_open(sb); if (p_bd->sector_size < sb->s_blocksize) { - printk(KERN_INFO "EXFAT: mount failed - sector size %d less than blocksize %ld\n", + pr_info("EXFAT: mount failed - sector size %d less than blocksize %ld\n", p_bd->sector_size, sb->s_blocksize); ret = -EINVAL; goto out; From cc7eac1e4afdd151085be4d0341a155760388653 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 27 Jan 2020 17:37:42 +0900 Subject: [PATCH 0052/2231] usb: host: ehci-platform: add a quirk to avoid stuck Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting stuck very rarely after a full/low usb device was disconnected. To detect/recover from such a situation, the controllers require a special way which poll the EHCI PORTSC register and changes the OHCI functional state. So, this patch adds a polling timer into the ehci-platform driver, and if the ehci driver detects the issue by the EHCI PORTSC register, the ehci driver removes a companion device (= the OHCI controller) to change the OHCI functional state to USB Reset once. And then, the ehci driver adds the companion device again. Signed-off-by: Yoshihiro Shimoda Acked-by: Alan Stern Link: https://lore.kernel.org/r/1580114262-25029-1-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 127 +++++++++++++++++++++++++++++++ include/linux/usb/ehci_def.h | 2 +- 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 769749ca5961..e4fc3f66d43b 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -44,6 +46,9 @@ struct ehci_platform_priv { struct clk *clks[EHCI_MAX_CLKS]; struct reset_control *rsts; bool reset_on_resume; + bool quirk_poll; + struct timer_list poll_timer; + struct delayed_work poll_work; }; static const char hcd_name[] = "ehci-platform"; @@ -118,6 +123,111 @@ static struct usb_ehci_pdata ehci_platform_defaults = { .power_off = ehci_platform_power_off, }; +/** + * quirk_poll_check_port_status - Poll port_status if the device sticks + * @ehci: the ehci hcd pointer + * + * Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting + * stuck very rarely after a full/low usb device was disconnected. To + * detect such a situation, the controllers require a special way which poll + * the EHCI PORTSC register. + * + * Return: true if the controller's port_status indicated getting stuck + */ +static bool quirk_poll_check_port_status(struct ehci_hcd *ehci) +{ + u32 port_status = ehci_readl(ehci, &ehci->regs->port_status[0]); + + if (!(port_status & PORT_OWNER) && + (port_status & PORT_POWER) && + !(port_status & PORT_CONNECT) && + (port_status & PORT_LS_MASK)) + return true; + + return false; +} + +/** + * quirk_poll_rebind_companion - rebind comanion device to recover + * @ehci: the ehci hcd pointer + * + * Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting + * stuck very rarely after a full/low usb device was disconnected. To + * recover from such a situation, the controllers require changing the OHCI + * functional state. + */ +static void quirk_poll_rebind_companion(struct ehci_hcd *ehci) +{ + struct device *companion_dev; + struct usb_hcd *hcd = ehci_to_hcd(ehci); + + companion_dev = usb_of_get_companion_dev(hcd->self.controller); + if (!companion_dev) + return; + + device_release_driver(companion_dev); + if (device_attach(companion_dev) < 0) + ehci_err(ehci, "%s: failed\n", __func__); + + put_device(companion_dev); +} + +static void quirk_poll_work(struct work_struct *work) +{ + struct ehci_platform_priv *priv = + container_of(to_delayed_work(work), struct ehci_platform_priv, + poll_work); + struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd, + priv); + + /* check the status twice to reduce misdetection rate */ + if (!quirk_poll_check_port_status(ehci)) + return; + udelay(10); + if (!quirk_poll_check_port_status(ehci)) + return; + + ehci_dbg(ehci, "%s: detected getting stuck. rebind now!\n", __func__); + quirk_poll_rebind_companion(ehci); +} + +static void quirk_poll_timer(struct timer_list *t) +{ + struct ehci_platform_priv *priv = from_timer(priv, t, poll_timer); + struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd, + priv); + + if (quirk_poll_check_port_status(ehci)) { + /* + * Now scheduling the work for testing the port more. Note that + * updating the status is possible to be delayed when + * reconnection. So, this uses delayed work with 5 ms delay + * to avoid misdetection. + */ + schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(5)); + } + + mod_timer(&priv->poll_timer, jiffies + HZ); +} + +static void quirk_poll_init(struct ehci_platform_priv *priv) +{ + INIT_DELAYED_WORK(&priv->poll_work, quirk_poll_work); + timer_setup(&priv->poll_timer, quirk_poll_timer, 0); + mod_timer(&priv->poll_timer, jiffies + HZ); +} + +static void quirk_poll_end(struct ehci_platform_priv *priv) +{ + del_timer_sync(&priv->poll_timer); + cancel_delayed_work(&priv->poll_work); +} + +static const struct soc_device_attribute quirk_poll_match[] = { + { .family = "R-Car Gen3" }, + { /* sentinel*/ } +}; + static int ehci_platform_probe(struct platform_device *dev) { struct usb_hcd *hcd; @@ -176,6 +286,9 @@ static int ehci_platform_probe(struct platform_device *dev) "has-transaction-translator")) hcd->has_tt = 1; + if (soc_device_match(quirk_poll_match)) + priv->quirk_poll = true; + for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); if (IS_ERR(priv->clks[clk])) { @@ -247,6 +360,9 @@ static int ehci_platform_probe(struct platform_device *dev) device_enable_async_suspend(hcd->self.controller); platform_set_drvdata(dev, hcd); + if (priv->quirk_poll) + quirk_poll_init(priv); + return err; err_power: @@ -273,6 +389,9 @@ static int ehci_platform_remove(struct platform_device *dev) struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); int clk; + if (priv->quirk_poll) + quirk_poll_end(priv); + usb_remove_hcd(hcd); if (pdata->power_off) @@ -297,9 +416,13 @@ static int ehci_platform_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = to_platform_device(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); bool do_wakeup = device_may_wakeup(dev); int ret; + if (priv->quirk_poll) + quirk_poll_end(priv); + ret = ehci_suspend(hcd, do_wakeup); if (ret) return ret; @@ -331,6 +454,10 @@ static int ehci_platform_resume(struct device *dev) } ehci_resume(hcd, priv->reset_on_resume); + + if (priv->quirk_poll) + quirk_poll_init(priv); + return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index a15ce99dfc2d..78e006355557 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -151,7 +151,7 @@ struct ehci_regs { #define PORT_OWNER (1<<13) /* true: companion hc owns this port */ #define PORT_POWER (1<<12) /* true: has power (see PPC) */ #define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ -/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +#define PORT_LS_MASK (3<<10) /* Link status (SE0, K or J */ /* 9 reserved */ #define PORT_LPM (1<<9) /* LPM transaction */ #define PORT_RESET (1<<8) /* reset port */ From 85798543f55cd51cb57de4b6aa4ab6c05206d26f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 8 Feb 2020 16:50:22 +0000 Subject: [PATCH 0053/2231] usb: typec: ucsi: remove redundant assignment to variable num Variable num is being assigned with a value that is never read, it is assigned a new value later in a for-loop. The assignment is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200208165022.30429-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index d5a6aac86327..b1b72cb7af10 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -400,7 +400,7 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) struct typec_altmode_desc desc; struct ucsi_altmode alt[2]; u64 command; - int num = 1; + int num; int ret; int len; int j; From 334fb94c39e0ab5bb8fa61ff6e82fe6545b45ed7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 8 Feb 2020 16:31:32 +0000 Subject: [PATCH 0054/2231] usb: gadget: remove redundant assignment to variable status Variable status is being assigned with a value that is never read, it is assigned a new value immediately afterwards. The assignment is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200208163132.29592-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uac1_legacy.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index 6677ae932de0..349deae7cabd 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -752,8 +752,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) audio->out_ep = ep; audio->out_ep->desc = &as_out_ep_desc; - status = -ENOMEM; - /* copy descriptors, and track endpoint copies */ status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL, NULL); From 1f9f5a8193e697ae5a1dfb619ee554f037526255 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 8 Feb 2020 16:18:02 +0000 Subject: [PATCH 0055/2231] usb: cdns3: remove redundant assignment to pointer trb Pointer trb being assigned with a value that is never read, it is assigned a new value later on. The assignment is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200208161802.28846-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 736b0c6e27fe..3c05080a9ad5 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1380,7 +1380,7 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep, struct cdns3_request *priv_req) { struct cdns3_device *priv_dev = priv_ep->cdns3_dev; - struct cdns3_trb *trb = priv_req->trb; + struct cdns3_trb *trb; int current_index = 0; int handled = 0; int doorbell; From 36be4102b7da81cc9638307789ae81def3de4a3b Mon Sep 17 00:00:00 2001 From: Michael Straube Date: Mon, 10 Feb 2020 21:08:30 +0100 Subject: [PATCH 0056/2231] staging: rtl8188eu: rename variable pnetdev -> netdev Rename the local variable pnetdev in rtw_alloc_etherdev_with_old_priv to avoid hungarian notation and clear the last checkpatch warning in the file osdep_service.c. rtl8188eu/os_dep/osdep_service.c:32: WARNING: line over 80 characters Signed-off-by: Michael Straube Link: https://lore.kernel.org/r/20200210200830.22868-1-straube.linux@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8188eu/os_dep/osdep_service.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/rtl8188eu/os_dep/osdep_service.c b/drivers/staging/rtl8188eu/os_dep/osdep_service.c index 4ba2378a1bb8..4d6d0347ab8e 100644 --- a/drivers/staging/rtl8188eu/os_dep/osdep_service.c +++ b/drivers/staging/rtl8188eu/os_dep/osdep_service.c @@ -26,17 +26,17 @@ void _rtw_init_queue(struct __queue *pqueue) struct net_device *rtw_alloc_etherdev_with_old_priv(void *old_priv) { - struct net_device *pnetdev; + struct net_device *netdev; struct rtw_netdev_priv_indicator *pnpi; - pnetdev = alloc_etherdev_mq(sizeof(struct rtw_netdev_priv_indicator), 4); - if (!pnetdev) + netdev = alloc_etherdev_mq(sizeof(struct rtw_netdev_priv_indicator), 4); + if (!netdev) return NULL; - pnpi = netdev_priv(pnetdev); + pnpi = netdev_priv(netdev); pnpi->priv = old_priv; - return pnetdev; + return netdev; } void rtw_free_netdev(struct net_device *netdev) From 469e1906a1b121ecb0c2ef43f28a98fd9d453831 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sat, 8 Feb 2020 20:44:07 +0200 Subject: [PATCH 0057/2231] platform: constify properties in platform_device Constify 'struct property_entry *properties' in platform_device. It is always passed around as a pointer const struct. Reviewed-by: Andy Shevchenko Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200208184407.1294-2-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/platform_device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 276a03c24691..8e83c6ff140d 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -89,7 +89,7 @@ struct platform_device_info { size_t size_data; u64 dma_mask; - struct property_entry *properties; + const struct property_entry *properties; }; extern struct platform_device *platform_device_register_full( const struct platform_device_info *pdevinfo); From 901cff7cb96140a658a848a568b606ba764239bc Mon Sep 17 00:00:00 2001 From: Topi Miettinen Date: Thu, 23 Jan 2020 14:58:38 +0200 Subject: [PATCH 0058/2231] firmware_loader: load files from the mount namespace of init I have an experimental setup where almost every possible system service (even early startup ones) runs in separate namespace, using a dedicated, minimal file system. In process of minimizing the contents of the file systems with regards to modules and firmware files, I noticed that in my system, the firmware files are loaded from three different mount namespaces, those of systemd-udevd, init and systemd-networkd. The logic of the source namespace is not very clear, it seems to depend on the driver, but the namespace of the current process is used. So, this patch tries to make things a bit clearer and changes the loading of firmware files only from the mount namespace of init. This may also improve security, though I think that using firmware files as attack vector could be too impractical anyway. Later, it might make sense to make the mount namespace configurable, for example with a new file in /proc/sys/kernel/firmware_config/. That would allow a dedicated file system only for firmware files and those need not be present anywhere else. This configurability would make more sense if made also for kernel modules and /sbin/modprobe. Modules are already loaded from init namespace (usermodehelper uses kthreadd namespace) except when directly loaded by systemd-udevd. Instead of using the mount namespace of the current process to load firmware files, use the mount namespace of init process. Link: https://lore.kernel.org/lkml/bb46ebae-4746-90d9-ec5b-fce4c9328c86@gmail.com/ Link: https://lore.kernel.org/lkml/0e3f7653-c59d-9341-9db2-c88f5b988c68@gmail.com/ Signed-off-by: Topi Miettinen Link: https://lore.kernel.org/r/20200123125839.37168-1-toiwoton@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/main.c | 6 +- fs/exec.c | 26 +++ include/linux/fs.h | 2 + tools/testing/selftests/firmware/Makefile | 9 +- .../testing/selftests/firmware/fw_namespace.c | 151 ++++++++++++++++++ .../selftests/firmware/fw_run_tests.sh | 4 + 6 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 tools/testing/selftests/firmware/fw_namespace.c diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 57133a9dad09..59d1dc322080 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -493,8 +493,10 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, } fw_priv->size = 0; - rc = kernel_read_file_from_path(path, &buffer, &size, - msize, id); + + /* load firmware files from the mount namespace of init */ + rc = kernel_read_file_from_path_initns(path, &buffer, + &size, msize, id); if (rc) { if (rc != -ENOENT) dev_warn(device, "loading %s failed with error %d\n", diff --git a/fs/exec.c b/fs/exec.c index db17be51b112..688c824cdac8 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -985,6 +985,32 @@ int kernel_read_file_from_path(const char *path, void **buf, loff_t *size, } EXPORT_SYMBOL_GPL(kernel_read_file_from_path); +int kernel_read_file_from_path_initns(const char *path, void **buf, + loff_t *size, loff_t max_size, + enum kernel_read_file_id id) +{ + struct file *file; + struct path root; + int ret; + + if (!path || !*path) + return -EINVAL; + + task_lock(&init_task); + get_fs_root(init_task.fs, &root); + task_unlock(&init_task); + + file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0); + path_put(&root); + if (IS_ERR(file)) + return PTR_ERR(file); + + ret = kernel_read_file(file, buf, size, max_size, id); + fput(file); + return ret; +} +EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns); + int kernel_read_file_from_fd(int fd, void **buf, loff_t *size, loff_t max_size, enum kernel_read_file_id id) { diff --git a/include/linux/fs.h b/include/linux/fs.h index 3cd4fe6b845e..f814ccd8d929 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3012,6 +3012,8 @@ extern int kernel_read_file(struct file *, void **, loff_t *, loff_t, enum kernel_read_file_id); extern int kernel_read_file_from_path(const char *, void **, loff_t *, loff_t, enum kernel_read_file_id); +extern int kernel_read_file_from_path_initns(const char *, void **, loff_t *, loff_t, + enum kernel_read_file_id); extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t, enum kernel_read_file_id); extern ssize_t kernel_read(struct file *, void *, size_t, loff_t *); diff --git a/tools/testing/selftests/firmware/Makefile b/tools/testing/selftests/firmware/Makefile index 012b2cf69c11..40211cd8f0e6 100644 --- a/tools/testing/selftests/firmware/Makefile +++ b/tools/testing/selftests/firmware/Makefile @@ -1,13 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only # Makefile for firmware loading selftests - -# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" -all: +CFLAGS = -Wall \ + -O2 TEST_PROGS := fw_run_tests.sh TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_lib.sh +TEST_GEN_FILES := fw_namespace include ../lib.mk - -# Nothing to clean up. -clean: diff --git a/tools/testing/selftests/firmware/fw_namespace.c b/tools/testing/selftests/firmware/fw_namespace.c new file mode 100644 index 000000000000..5ebc1aec7923 --- /dev/null +++ b/tools/testing/selftests/firmware/fw_namespace.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Test triggering of loading of firmware from different mount + * namespaces. Expect firmware to be always loaded from the mount + * namespace of PID 1. */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CLONE_NEWNS +# define CLONE_NEWNS 0x00020000 +#endif + +static char *fw_path = NULL; + +static void die(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (fw_path) + unlink(fw_path); + umount("/lib/firmware"); + exit(EXIT_FAILURE); +} + +static void trigger_fw(const char *fw_name, const char *sys_path) +{ + int fd; + + fd = open(sys_path, O_WRONLY); + if (fd < 0) + die("open failed: %s\n", + strerror(errno)); + if (write(fd, fw_name, strlen(fw_name)) != strlen(fw_name)) + exit(EXIT_FAILURE); + close(fd); +} + +static void setup_fw(const char *fw_path) +{ + int fd; + const char fw[] = "ABCD0123"; + + fd = open(fw_path, O_WRONLY | O_CREAT, 0600); + if (fd < 0) + die("open failed: %s\n", + strerror(errno)); + if (write(fd, fw, sizeof(fw) -1) != sizeof(fw) -1) + die("write failed: %s\n", + strerror(errno)); + close(fd); +} + +static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_fw_in_parent_ns) +{ + pid_t child; + + if (block_fw_in_parent_ns) + if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1) + die("blocking firmware in parent ns failed\n"); + + child = fork(); + if (child == -1) { + die("fork failed: %s\n", + strerror(errno)); + } + if (child != 0) { /* parent */ + pid_t pid; + int status; + + pid = waitpid(child, &status, 0); + if (pid == -1) { + die("waitpid failed: %s\n", + strerror(errno)); + } + if (pid != child) { + die("waited for %d got %d\n", + child, pid); + } + if (!WIFEXITED(status)) { + die("child did not terminate cleanly\n"); + } + if (block_fw_in_parent_ns) + umount("/lib/firmware"); + return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; + } + + if (unshare(CLONE_NEWNS) != 0) { + die("unshare(CLONE_NEWNS) failed: %s\n", + strerror(errno)); + } + if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) == -1) + die("remount root in child ns failed\n"); + + if (!block_fw_in_parent_ns) { + if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1) + die("blocking firmware in child ns failed\n"); + } else + umount("/lib/firmware"); + + trigger_fw(fw_name, sys_path); + + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + const char *fw_name = "test-firmware.bin"; + char *sys_path; + if (argc != 2) + die("usage: %s sys_path\n", argv[0]); + + /* Mount tmpfs to /lib/firmware so we don't have to assume + that it is writable for us.*/ + if (mount("test", "/lib/firmware", "tmpfs", 0, NULL) == -1) + die("mounting tmpfs to /lib/firmware failed\n"); + + sys_path = argv[1]; + asprintf(&fw_path, "/lib/firmware/%s", fw_name); + + setup_fw(fw_path); + + setvbuf(stdout, NULL, _IONBF, 0); + /* Positive case: firmware in PID1 mount namespace */ + printf("Testing with firmware in parent namespace (assumed to be same file system as PID1)\n"); + if (!test_fw_in_ns(fw_name, sys_path, false)) + die("error: failed to access firmware\n"); + + /* Negative case: firmware in child mount namespace, expected to fail */ + printf("Testing with firmware in child namespace\n"); + if (test_fw_in_ns(fw_name, sys_path, true)) + die("error: firmware access did not fail\n"); + + unlink(fw_path); + free(fw_path); + umount("/lib/firmware"); + exit(EXIT_SUCCESS); +} diff --git a/tools/testing/selftests/firmware/fw_run_tests.sh b/tools/testing/selftests/firmware/fw_run_tests.sh index 8e14d555c197..777377078d5e 100755 --- a/tools/testing/selftests/firmware/fw_run_tests.sh +++ b/tools/testing/selftests/firmware/fw_run_tests.sh @@ -61,6 +61,10 @@ run_test_config_0003() check_mods check_setup +echo "Running namespace test: " +$TEST_DIR/fw_namespace $DIR/trigger_request +echo "OK" + if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then run_test_config_0001 run_test_config_0002 From 4d49177f2cd7dce6e669584a5114b073b26a0d0f Mon Sep 17 00:00:00 2001 From: Markus Reichl Date: Mon, 3 Feb 2020 12:00:33 +0100 Subject: [PATCH 0059/2231] regulator: mp8859: add supply entry Add vin_supply to the regulator description to support a nice regulator tree. Signed-off-by: Markus Reichl Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20200203110034.1448-1-m.reichl@fivetechno.de Signed-off-by: Mark Brown --- drivers/regulator/mp8859.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/mp8859.c b/drivers/regulator/mp8859.c index 1d26b506ee5b..6ed987648188 100644 --- a/drivers/regulator/mp8859.c +++ b/drivers/regulator/mp8859.c @@ -95,6 +95,7 @@ static const struct regulator_desc mp8859_regulators[] = { .id = 0, .type = REGULATOR_VOLTAGE, .name = "mp8859_dcdc", + .supply_name = "vin", .of_match = of_match_ptr("mp8859_dcdc"), .n_voltages = VOL_MAX_IDX + 1, .linear_ranges = mp8859_dcdc_ranges, From a33b25f57ddeb8545dab6ffee6be0f38d89d42c6 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Thu, 6 Feb 2020 19:18:37 +0530 Subject: [PATCH 0060/2231] regulator: da9063: fix code formatting warnings and errors This commit fixes following errors & warnings in this driver as reported by checkpatch.pl: - WARNING: Prefer 'unsigned int' to bare use of 'unsigned' - WARNING: line over 80 characters - ERROR: space prohibited before that ',' (ctx:WxW) - ERROR: code indent should use tabs where possible - WARNING: Block comments use * on subsequent lines Signed-off-by: Rishi Gupta Link: https://lore.kernel.org/r/1580996917-28494-1-git-send-email-gupt21@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/da9063-regulator.c | 58 ++++++++++++++++------------ 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index 2aceb3b7afc2..ae54c76a8580 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -66,7 +66,7 @@ struct da9063_regulator_data { }; struct da9063_regulators_pdata { - unsigned n_regulators; + unsigned int n_regulators; struct da9063_regulator_data *regulator_data; }; @@ -131,7 +131,7 @@ struct da9063_regulator_info { /* Defines asignment of regulators info table to chip model */ struct da9063_dev_model { const struct da9063_regulator_info *regulator_info; - unsigned n_regulators; + unsigned int n_regulators; enum da9063_type type; }; @@ -150,7 +150,7 @@ struct da9063_regulator { /* Encapsulates all information for the regulators driver */ struct da9063_regulators { - unsigned n_regulators; + unsigned int n_regulators; /* Array size to be defined during init. Keep at end. */ struct da9063_regulator regulator[0]; }; @@ -165,38 +165,46 @@ enum { /* Regulator operations */ -/* Current limits array (in uA) for BCORE1, BCORE2, BPRO. - Entry indexes corresponds to register values. */ +/* + * Current limits array (in uA) for BCORE1, BCORE2, BPRO. + * Entry indexes corresponds to register values. + */ static const unsigned int da9063_buck_a_limits[] = { 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000, 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000 }; -/* Current limits array (in uA) for BMEM, BIO, BPERI. - Entry indexes corresponds to register values. */ +/* + * Current limits array (in uA) for BMEM, BIO, BPERI. + * Entry indexes corresponds to register values. + */ static const unsigned int da9063_buck_b_limits[] = { 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000 }; -/* Current limits array (in uA) for merged BCORE1 and BCORE2. - Entry indexes corresponds to register values. */ +/* + * Current limits array (in uA) for merged BCORE1 and BCORE2. + * Entry indexes corresponds to register values. + */ static const unsigned int da9063_bcores_merged_limits[] = { 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2200000, 2400000, 2600000, 2800000, 3000000, 3200000, 3400000, 3600000, 3800000, 4000000 }; -/* Current limits array (in uA) for merged BMEM and BIO. - Entry indexes corresponds to register values. */ +/* + * Current limits array (in uA) for merged BMEM and BIO. + * Entry indexes corresponds to register values. + */ static const unsigned int da9063_bmem_bio_merged_limits[] = { 3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000, 4600000, 4800000, 5000000, 5200000, 5400000, 5600000, 5800000, 6000000 }; -static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned mode) +static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) { struct da9063_regulator *regl = rdev_get_drvdata(rdev); - unsigned val; + unsigned int val; switch (mode) { case REGULATOR_MODE_FAST: @@ -221,7 +229,7 @@ static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned mode) * There are 3 modes to map to: FAST, NORMAL, and STANDBY. */ -static unsigned da9063_buck_get_mode(struct regulator_dev *rdev) +static unsigned int da9063_buck_get_mode(struct regulator_dev *rdev) { struct da9063_regulator *regl = rdev_get_drvdata(rdev); struct regmap_field *field; @@ -271,10 +279,10 @@ static unsigned da9063_buck_get_mode(struct regulator_dev *rdev) * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state. */ -static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned mode) +static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode) { struct da9063_regulator *regl = rdev_get_drvdata(rdev); - unsigned val; + unsigned int val; switch (mode) { case REGULATOR_MODE_NORMAL: @@ -290,7 +298,7 @@ static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned mode) return regmap_field_write(regl->sleep, val); } -static unsigned da9063_ldo_get_mode(struct regulator_dev *rdev) +static unsigned int da9063_ldo_get_mode(struct regulator_dev *rdev) { struct da9063_regulator *regl = rdev_get_drvdata(rdev); struct regmap_field *field; @@ -383,7 +391,8 @@ static int da9063_suspend_disable(struct regulator_dev *rdev) return regmap_field_write(regl->suspend, 0); } -static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev, unsigned mode) +static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) { struct da9063_regulator *regl = rdev_get_drvdata(rdev); int val; @@ -405,10 +414,11 @@ static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev, unsigned mod return regmap_field_write(regl->mode, val); } -static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, unsigned mode) +static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) { struct da9063_regulator *regl = rdev_get_drvdata(rdev); - unsigned val; + unsigned int val; switch (mode) { case REGULATOR_MODE_NORMAL: @@ -593,7 +603,7 @@ static irqreturn_t da9063_ldo_lim_event(int irq, void *data) struct da9063_regulators *regulators = data; struct da9063 *hw = regulators->regulator[0].hw; struct da9063_regulator *regl; - int bits, i , ret; + int bits, i, ret; ret = regmap_read(hw->regmap, DA9063_REG_STATUS_D, &bits); if (ret < 0) @@ -605,10 +615,10 @@ static irqreturn_t da9063_ldo_lim_event(int irq, void *data) continue; if (BIT(regl->info->oc_event.lsb) & bits) { - regulator_lock(regl->rdev); + regulator_lock(regl->rdev); regulator_notifier_call_chain(regl->rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); - regulator_unlock(regl->rdev); + regulator_unlock(regl->rdev); } } @@ -833,7 +843,7 @@ static int da9063_regulator_probe(struct platform_device *pdev) if (regl->info->suspend_sleep.reg) { regl->suspend_sleep = devm_regmap_field_alloc(&pdev->dev, - da9063->regmap, regl->info->suspend_sleep); + da9063->regmap, regl->info->suspend_sleep); if (IS_ERR(regl->suspend_sleep)) return PTR_ERR(regl->suspend_sleep); } From 6d8d840b214e12be6556ed7bee803d9280c54f3b Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Thu, 6 Feb 2020 19:19:56 +0530 Subject: [PATCH 0061/2231] regulator: da9063: remove redundant return statement The devm_request_threaded_irq() already returns 0 on success and negative error code on failure. So return from this itself can be used while preserving error log in case of failure. Signed-off-by: Rishi Gupta Link: https://lore.kernel.org/r/1580996996-28798-1-git-send-email-gupt21@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/da9063-regulator.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index ae54c76a8580..aaa994293e9b 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -877,12 +877,10 @@ static int da9063_regulator_probe(struct platform_device *pdev) NULL, da9063_ldo_lim_event, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "LDO_LIM", regulators); - if (ret) { + if (ret) dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n"); - return ret; - } - return 0; + return ret; } static struct platform_driver da9063_regulator_driver = { From 65c38513528ffe673a0a9568593b475b16a7031c Mon Sep 17 00:00:00 2001 From: Saravanan Sekar Date: Tue, 4 Feb 2020 12:04:17 +0100 Subject: [PATCH 0062/2231] dt-bindings: regulator: add document bindings for mp5416 Add device tree binding information for mp5416 regulator driver. Signed-off-by: Saravanan Sekar Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200204110419.25933-2-sravanhome@gmail.com Signed-off-by: Mark Brown --- .../bindings/regulator/mps,mp5416.yaml | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/mps,mp5416.yaml diff --git a/Documentation/devicetree/bindings/regulator/mps,mp5416.yaml b/Documentation/devicetree/bindings/regulator/mps,mp5416.yaml new file mode 100644 index 000000000000..f0acce2029fd --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/mps,mp5416.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/mps,mp5416.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Monolithic Power System MP5416 PMIC + +maintainers: + - Saravanan Sekar + +properties: + $nodename: + pattern: "^pmic@[0-9a-f]{1,2}$" + compatible: + enum: + - mps,mp5416 + + reg: + maxItems: 1 + + regulators: + type: object + description: | + list of regulators provided by this controller, must be named + after their hardware counterparts BUCK[1-4] and LDO[1-4] + + patternProperties: + "^buck[1-4]$": + allOf: + - $ref: "regulator.yaml#" + type: object + + "^ldo[1-4]$": + allOf: + - $ref: "regulator.yaml#" + type: object + + additionalProperties: false + additionalProperties: false + +required: + - compatible + - reg + - regulators + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pmic@69 { + compatible = "mps,mp5416"; + reg = <0x69>; + + regulators { + + buck1 { + regulator-name = "buck1"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <2187500>; + regulator-min-microamp = <3800000>; + regulator-max-microamp = <6800000>; + regulator-boot-on; + }; + + ldo2 { + regulator-name = "ldo2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3975000>; + }; + }; + }; + }; +... From a273188b87fd7afc9b1a0f814452ecfb4e764f62 Mon Sep 17 00:00:00 2001 From: Saravanan Sekar Date: Tue, 4 Feb 2020 12:04:18 +0100 Subject: [PATCH 0063/2231] regulator: mp5416: add mp5416 regulator driver Adding regulator driver for the device mp5416. The MP5416 PMIC device contains four DC-DC buck converters and five regulators, accessed over I2C. Signed-off-by: Saravanan Sekar Link: https://lore.kernel.org/r/20200204110419.25933-3-sravanhome@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 ++ drivers/regulator/Makefile | 1 + drivers/regulator/mp5416.c | 245 +++++++++++++++++++++++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 drivers/regulator/mp5416.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 074a2ef55943..b8ae513514a8 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -613,6 +613,16 @@ config REGULATOR_MCP16502 through the regulator interface. In addition it enables suspend-to-ram/standby transition. +config REGULATOR_MP5416 + tristate "Monolithic MP5416 PMIC" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support the MP5416 PMIC. This will enable supports + the software controllable 4 buck and 4 LDO regulators. + Say M here if you want to include support for the regulator as a + module. + config REGULATOR_MP8859 tristate "MPS MP8859 regulator driver" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index c0d6b96ebd78..bc69d6481646 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o +obj-$(CONFIG_REGULATOR_MP5416) += mp5416.o obj-$(CONFIG_REGULATOR_MP8859) += mp8859.o obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o diff --git a/drivers/regulator/mp5416.c b/drivers/regulator/mp5416.c new file mode 100644 index 000000000000..7954ad17249b --- /dev/null +++ b/drivers/regulator/mp5416.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// mp5416.c - regulator driver for mps mp5416 +// +// Copyright 2020 Monolithic Power Systems, Inc +// +// Author: Saravanan Sekar + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MP5416_REG_CTL0 0x00 +#define MP5416_REG_CTL1 0x01 +#define MP5416_REG_CTL2 0x02 +#define MP5416_REG_ILIM 0x03 +#define MP5416_REG_BUCK1 0x04 +#define MP5416_REG_BUCK2 0x05 +#define MP5416_REG_BUCK3 0x06 +#define MP5416_REG_BUCK4 0x07 +#define MP5416_REG_LDO1 0x08 +#define MP5416_REG_LDO2 0x09 +#define MP5416_REG_LDO3 0x0a +#define MP5416_REG_LDO4 0x0b + +#define MP5416_REGULATOR_EN BIT(7) +#define MP5416_MASK_VSET 0x7f +#define MP5416_MASK_BUCK1_ILIM 0xc0 +#define MP5416_MASK_BUCK2_ILIM 0x0c +#define MP5416_MASK_BUCK3_ILIM 0x30 +#define MP5416_MASK_BUCK4_ILIM 0x03 +#define MP5416_MASK_DVS_SLEWRATE 0xc0 + +/* values in uV */ +#define MP5416_VOLT1_MIN 600000 +#define MP5416_VOLT1_MAX 2187500 +#define MP5416_VOLT1_STEP 12500 +#define MP5416_VOLT2_MIN 800000 +#define MP5416_VOLT2_MAX 3975000 +#define MP5416_VOLT2_STEP 25000 + +#define MP5416_VOLT1_RANGE \ + ((MP5416_VOLT1_MAX - MP5416_VOLT1_MIN)/MP5416_VOLT1_STEP + 1) +#define MP5416_VOLT2_RANGE \ + ((MP5416_VOLT2_MAX - MP5416_VOLT2_MIN)/MP5416_VOLT2_STEP + 1) + +#define MP5416BUCK(_name, _id, _ilim, _dreg, _dval, _vsel) \ + [MP5416_BUCK ## _id] = { \ + .id = MP5416_BUCK ## _id, \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .ops = &mp5416_buck_ops, \ + .min_uV = MP5416_VOLT ##_vsel## _MIN, \ + .uV_step = MP5416_VOLT ##_vsel## _STEP, \ + .n_voltages = MP5416_VOLT ##_vsel## _RANGE, \ + .curr_table = _ilim, \ + .n_current_limits = ARRAY_SIZE(_ilim), \ + .csel_reg = MP5416_REG_ILIM, \ + .csel_mask = MP5416_MASK_BUCK ## _id ##_ILIM, \ + .vsel_reg = MP5416_REG_BUCK ## _id, \ + .vsel_mask = MP5416_MASK_VSET, \ + .enable_reg = MP5416_REG_BUCK ## _id, \ + .enable_mask = MP5416_REGULATOR_EN, \ + .active_discharge_on = _dval, \ + .active_discharge_reg = _dreg, \ + .active_discharge_mask = _dval, \ + .owner = THIS_MODULE, \ + } + +#define MP5416LDO(_name, _id) \ + [MP5416_LDO ## _id] = { \ + .id = MP5416_LDO ## _id, \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .ops = &mp5416_ldo_ops, \ + .min_uV = MP5416_VOLT2_MIN, \ + .uV_step = MP5416_VOLT2_STEP, \ + .n_voltages = MP5416_VOLT2_RANGE, \ + .vsel_reg = MP5416_REG_LDO ##_id, \ + .vsel_mask = MP5416_MASK_VSET, \ + .enable_reg = MP5416_REG_LDO ##_id, \ + .enable_mask = MP5416_REGULATOR_EN, \ + .active_discharge_on = BIT(_id), \ + .active_discharge_reg = MP5416_REG_CTL2, \ + .active_discharge_mask = BIT(_id), \ + .owner = THIS_MODULE, \ + } + +enum mp5416_regulators { + MP5416_BUCK1, + MP5416_BUCK2, + MP5416_BUCK3, + MP5416_BUCK4, + MP5416_LDO1, + MP5416_LDO2, + MP5416_LDO3, + MP5416_LDO4, + MP5416_MAX_REGULATORS, +}; + +static const struct regmap_config mp5416_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0d, +}; + +/* Current limits array (in uA) + * ILIM1 & ILIM3 + */ +static const unsigned int mp5416_I_limits1[] = { + 3800000, 4600000, 5600000, 6800000 +}; + +/* ILIM2 & ILIM4 */ +static const unsigned int mp5416_I_limits2[] = { + 2200000, 3200000, 4200000, 5200000 +}; + +static int mp5416_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay); + +static const struct regulator_ops mp5416_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, +}; + +static const struct regulator_ops mp5416_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_current_limit = regulator_get_current_limit_regmap, + .set_current_limit = regulator_set_current_limit_regmap, + .set_ramp_delay = mp5416_set_ramp_delay, +}; + +static struct regulator_desc mp5416_regulators_desc[MP5416_MAX_REGULATORS] = { + MP5416BUCK("buck1", 1, mp5416_I_limits1, MP5416_REG_CTL1, BIT(0), 1), + MP5416BUCK("buck2", 2, mp5416_I_limits2, MP5416_REG_CTL1, BIT(1), 2), + MP5416BUCK("buck3", 3, mp5416_I_limits1, MP5416_REG_CTL1, BIT(2), 1), + MP5416BUCK("buck4", 4, mp5416_I_limits2, MP5416_REG_CTL2, BIT(5), 2), + MP5416LDO("ldo1", 1), + MP5416LDO("ldo2", 2), + MP5416LDO("ldo3", 3), + MP5416LDO("ldo4", 4), +}; + +/* + * DVS ramp rate BUCK1 to BUCK4 + * 00: 32mV/us + * 01: 16mV/us + * 10: 8mV/us + * 11: 4mV/us + */ +static int mp5416_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + unsigned int ramp_val; + + if (ramp_delay > 32000 || ramp_delay < 0) + return -EINVAL; + + if (ramp_delay <= 4000) + ramp_val = 3; + else if (ramp_delay <= 8000) + ramp_val = 2; + else if (ramp_delay <= 16000) + ramp_val = 1; + else + ramp_val = 0; + + return regmap_update_bits(rdev->regmap, MP5416_REG_CTL2, + MP5416_MASK_DVS_SLEWRATE, ramp_val << 6); +} + +static int mp5416_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regulator_config config = { NULL, }; + struct regulator_dev *rdev; + struct regmap *regmap; + int i; + + regmap = devm_regmap_init_i2c(client, &mp5416_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to allocate regmap!\n"); + return PTR_ERR(regmap); + } + + config.dev = dev; + config.regmap = regmap; + + for (i = 0; i < MP5416_MAX_REGULATORS; i++) { + rdev = devm_regulator_register(dev, + &mp5416_regulators_desc[i], + &config); + if (IS_ERR(rdev)) { + dev_err(dev, "Failed to register regulator!\n"); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct of_device_id mp5416_of_match[] = { + { .compatible = "mps,mp5416" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mp5416_of_match); + +static const struct i2c_device_id mp5416_id[] = { + { "mp5416", }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mp5416_id); + +static struct i2c_driver mp5416_regulator_driver = { + .driver = { + .name = "mp5416", + .of_match_table = of_match_ptr(mp5416_of_match), + }, + .probe_new = mp5416_i2c_probe, + .id_table = mp5416_id, +}; +module_i2c_driver(mp5416_regulator_driver); + +MODULE_AUTHOR("Saravanan Sekar "); +MODULE_DESCRIPTION("MP5416 PMIC regulator driver"); +MODULE_LICENSE("GPL"); From c1e1fa0ae5ba99f502bd2f5a4fd34d0ea22f1fdf Mon Sep 17 00:00:00 2001 From: Saravanan Sekar Date: Tue, 4 Feb 2020 12:04:19 +0100 Subject: [PATCH 0064/2231] MAINTAINERS: Add entry for mp5416 PMIC driver Add MAINTAINERS entry for Monolithic Power Systems mp5416 PMIC driver. Signed-off-by: Saravanan Sekar Link: https://lore.kernel.org/r/20200204110419.25933-4-sravanhome@gmail.com Signed-off-by: Mark Brown --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 38fe2f3f7b6f..060d48e5615c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11259,7 +11259,8 @@ F: drivers/tty/mxser.* MONOLITHIC POWER SYSTEM PMIC DRIVER M: Saravanan Sekar S: Maintained -F: Documentation/devicetree/bindings/regulator/mpq7920.yaml +F: Documentation/devicetree/bindings/regulator/mps,mp*.yaml +F: drivers/regulator/mp5416.c F: drivers/regulator/mpq7920.c F: drivers/regulator/mpq7920.h From ece9ef3b60cbc36237138af456b236d3d1d9b771 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 4 Feb 2020 11:12:04 -0800 Subject: [PATCH 0065/2231] spi: spi-geni-qcom: Let firmware specify irq trigger flags We don't need to force IRQF_TRIGGER_HIGH here as the DT or ACPI tables should take care of this for us. Just use 0 instead so that we use the flags from the firmware. Cc: Girish Mahadevan Cc: Dilip Kota Cc: Alok Chauhan Cc: Douglas Anderson Signed-off-by: Stephen Boyd Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20200204191206.97036-2-swboyd@chromium.org Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 6f3d64a1a2b3..46e501fc87f3 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -585,8 +585,7 @@ static int spi_geni_probe(struct platform_device *pdev) if (ret) goto spi_geni_probe_runtime_disable; - ret = request_irq(mas->irq, geni_spi_isr, - IRQF_TRIGGER_HIGH, "spi_geni", spi); + ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(&pdev->dev), spi); if (ret) goto spi_geni_probe_runtime_disable; From ea1e5b3354b1610ac16a780535fbdabfda9e6912 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 4 Feb 2020 11:12:05 -0800 Subject: [PATCH 0066/2231] spi: spi-geni-qcom: Grow a dev pointer to simplify code Some lines are long here. Use a struct dev pointer to shorten lines and simplify code. The clk_get() call can fail because of EPROBE_DEFER problems too, so just remove the error print message because it isn't useful. Cc: Girish Mahadevan Cc: Dilip Kota Cc: Alok Chauhan Cc: Douglas Anderson Signed-off-by: Stephen Boyd Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20200204191206.97036-3-swboyd@chromium.org Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 46e501fc87f3..f0ca7f5ae714 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -536,6 +536,7 @@ static int spi_geni_probe(struct platform_device *pdev) struct spi_geni_master *mas; void __iomem *base; struct clk *clk; + struct device *dev = &pdev->dev; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -545,28 +546,25 @@ static int spi_geni_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - clk = devm_clk_get(&pdev->dev, "se"); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "Err getting SE Core clk %ld\n", - PTR_ERR(clk)); + clk = devm_clk_get(dev, "se"); + if (IS_ERR(clk)) return PTR_ERR(clk); - } - spi = spi_alloc_master(&pdev->dev, sizeof(*mas)); + spi = spi_alloc_master(dev, sizeof(*mas)); if (!spi) return -ENOMEM; platform_set_drvdata(pdev, spi); mas = spi_master_get_devdata(spi); mas->irq = irq; - mas->dev = &pdev->dev; - mas->se.dev = &pdev->dev; - mas->se.wrapper = dev_get_drvdata(pdev->dev.parent); + mas->dev = dev; + mas->se.dev = dev; + mas->se.wrapper = dev_get_drvdata(dev->parent); mas->se.base = base; mas->se.clk = clk; spi->bus_num = -1; - spi->dev.of_node = pdev->dev.of_node; + spi->dev.of_node = dev->of_node; spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH; spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); spi->num_chipselect = 4; @@ -579,13 +577,13 @@ static int spi_geni_probe(struct platform_device *pdev) init_completion(&mas->xfer_done); spin_lock_init(&mas->lock); - pm_runtime_enable(&pdev->dev); + pm_runtime_enable(dev); ret = spi_geni_init(mas); if (ret) goto spi_geni_probe_runtime_disable; - ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(&pdev->dev), spi); + ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi); if (ret) goto spi_geni_probe_runtime_disable; @@ -597,7 +595,7 @@ static int spi_geni_probe(struct platform_device *pdev) spi_geni_probe_free_irq: free_irq(mas->irq, spi); spi_geni_probe_runtime_disable: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); spi_master_put(spi); return ret; } From 8196f7bcc2adf21f83d82691d537fcef7abaa1c7 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 3 Feb 2020 14:50:47 +0100 Subject: [PATCH 0067/2231] spi: stm32-qspi: defer probe for reset controller Changes stm32 QSPI driver to defer its probe operation when a reset controller device have not yet probed but is registered in the system. Signed-off-by: Etienne Carriere Signed-off-by: Patrice Chotard Link: https://lore.kernel.org/r/20200203135048.1299-2-patrice.chotard@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 4ef569b47aa6..13bb64bf4c8f 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -615,7 +615,11 @@ static int stm32_qspi_probe(struct platform_device *pdev) } rstc = devm_reset_control_get_exclusive(dev, NULL); - if (!IS_ERR(rstc)) { + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + if (ret == -EPROBE_DEFER) + goto err; + } else { reset_control_assert(rstc); udelay(2); reset_control_deassert(rstc); From 875014cb79ea4251e8800994640be1a8012b4133 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 4 Feb 2020 11:12:06 -0800 Subject: [PATCH 0068/2231] spi: spi-geni-qcom: Drop of.h include This driver doesn't call any DT functions like of_get_property(). Remove the of.h include as it isn't used. Cc: Girish Mahadevan Cc: Dilip Kota Cc: Alok Chauhan Cc: Douglas Anderson Signed-off-by: Stephen Boyd Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/20200204191206.97036-4-swboyd@chromium.org Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index f0ca7f5ae714..c3972424af71 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include From 4a08d6c8662472121b0ddc29180ab3c94c34f586 Mon Sep 17 00:00:00 2001 From: Lionel Debieve Date: Mon, 3 Feb 2020 14:50:48 +0100 Subject: [PATCH 0069/2231] spi: stm32-qspi: properly manage probe errors Fix resource release issues when driver probe operation fails. Signed-off-by: Lionel Debieve Signed-off-by: Etienne Carriere Signed-off-by: Patrice Chotard Link: https://lore.kernel.org/r/20200203135048.1299-3-patrice.chotard@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 13bb64bf4c8f..d066f5144c3e 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -565,7 +565,7 @@ static int stm32_qspi_probe(struct platform_device *pdev) qspi->io_base = devm_ioremap_resource(dev, res); if (IS_ERR(qspi->io_base)) { ret = PTR_ERR(qspi->io_base); - goto err; + goto err_master_put; } qspi->phys_base = res->start; @@ -574,24 +574,26 @@ static int stm32_qspi_probe(struct platform_device *pdev) qspi->mm_base = devm_ioremap_resource(dev, res); if (IS_ERR(qspi->mm_base)) { ret = PTR_ERR(qspi->mm_base); - goto err; + goto err_master_put; } qspi->mm_size = resource_size(res); if (qspi->mm_size > STM32_QSPI_MAX_MMAP_SZ) { ret = -EINVAL; - goto err; + goto err_master_put; } irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto err_master_put; + } ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0, dev_name(dev), qspi); if (ret) { dev_err(dev, "failed to request irq\n"); - goto err; + goto err_master_put; } init_completion(&qspi->data_completion); @@ -599,26 +601,26 @@ static int stm32_qspi_probe(struct platform_device *pdev) qspi->clk = devm_clk_get(dev, NULL); if (IS_ERR(qspi->clk)) { ret = PTR_ERR(qspi->clk); - goto err; + goto err_master_put; } qspi->clk_rate = clk_get_rate(qspi->clk); if (!qspi->clk_rate) { ret = -EINVAL; - goto err; + goto err_master_put; } ret = clk_prepare_enable(qspi->clk); if (ret) { dev_err(dev, "can not enable the clock\n"); - goto err; + goto err_master_put; } rstc = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(rstc)) { ret = PTR_ERR(rstc); if (ret == -EPROBE_DEFER) - goto err; + goto err_qspi_release; } else { reset_control_assert(rstc); udelay(2); @@ -629,7 +631,7 @@ static int stm32_qspi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qspi); ret = stm32_qspi_dma_setup(qspi); if (ret) - goto err; + goto err_qspi_release; mutex_init(&qspi->lock); @@ -645,8 +647,9 @@ static int stm32_qspi_probe(struct platform_device *pdev) if (!ret) return 0; -err: +err_qspi_release: stm32_qspi_release(qspi); +err_master_put: spi_master_put(qspi->ctrl); return ret; From 047980c582af665e1581e60ebf3e57977e621c23 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Mon, 10 Feb 2020 11:41:51 +0800 Subject: [PATCH 0070/2231] spi: add driver for ar934x spi controller This patch adds driver for SPI controller found in Qualcomm Atheros AR934x/QCA95xx SoCs. This controller is a superset of the already supported qca,ar7100-spi. Besides the bit-bang mode in spi-ath79.c, this new controller added a new "shift register" mode, allowing faster spi operations. Signed-off-by: Chuanhong Guo Link: https://lore.kernel.org/r/20200210034152.49063-2-gch981213@gmail.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 ++ drivers/spi/Makefile | 1 + drivers/spi/spi-ar934x.c | 235 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/spi/spi-ar934x.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d6ed0c355954..0434614d8201 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -62,6 +62,13 @@ config SPI_ALTERA help This is the driver for the Altera SPI Controller. +config SPI_AR934X + tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver" + depends on ATH79 || COMPILE_TEST + help + This enables support for the SPI controller present on the + Qualcomm Atheros AR934X/QCA95XX SoCs. + config SPI_ATH79 tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver" depends on ATH79 || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9b65ec5afc5e..9892bdfab15e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o # SPI master controller drivers (bus) obj-$(CONFIG_SPI_ALTERA) += spi-altera.o +obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o diff --git a/drivers/spi/spi-ar934x.c b/drivers/spi/spi-ar934x.c new file mode 100644 index 000000000000..d08dec09d423 --- /dev/null +++ b/drivers/spi/spi-ar934x.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// SPI controller driver for Qualcomm Atheros AR934x/QCA95xx SoCs +// +// Copyright (C) 2020 Chuanhong Guo +// +// Based on spi-mt7621.c: +// Copyright (C) 2011 Sergiy +// Copyright (C) 2011-2013 Gabor Juhos +// Copyright (C) 2014-2015 Felix Fietkau + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "spi-ar934x" + +#define AR934X_SPI_REG_FS 0x00 +#define AR934X_SPI_ENABLE BIT(0) + +#define AR934X_SPI_REG_IOC 0x08 +#define AR934X_SPI_IOC_INITVAL 0x70000 + +#define AR934X_SPI_REG_CTRL 0x04 +#define AR934X_SPI_CLK_MASK GENMASK(5, 0) + +#define AR934X_SPI_DATAOUT 0x10 + +#define AR934X_SPI_REG_SHIFT_CTRL 0x14 +#define AR934X_SPI_SHIFT_EN BIT(31) +#define AR934X_SPI_SHIFT_CS(n) BIT(28 + (n)) +#define AR934X_SPI_SHIFT_TERM 26 +#define AR934X_SPI_SHIFT_VAL(cs, term, count) \ + (AR934X_SPI_SHIFT_EN | AR934X_SPI_SHIFT_CS(cs) | \ + (term) << AR934X_SPI_SHIFT_TERM | (count)) + +#define AR934X_SPI_DATAIN 0x18 + +struct ar934x_spi { + struct spi_controller *ctlr; + void __iomem *base; + struct clk *clk; + unsigned int clk_freq; +}; + +static inline int ar934x_spi_clk_div(struct ar934x_spi *sp, unsigned int freq) +{ + int div = DIV_ROUND_UP(sp->clk_freq, freq * 2) - 1; + + if (div < 0) + return 0; + else if (div > AR934X_SPI_CLK_MASK) + return -EINVAL; + else + return div; +} + +static int ar934x_spi_setup(struct spi_device *spi) +{ + struct ar934x_spi *sp = spi_controller_get_devdata(spi->master); + + if ((spi->max_speed_hz == 0) || + (spi->max_speed_hz > (sp->clk_freq / 2))) { + spi->max_speed_hz = sp->clk_freq / 2; + } else if (spi->max_speed_hz < (sp->clk_freq / 128)) { + dev_err(&spi->dev, "spi clock is too low\n"); + return -EINVAL; + } + + return 0; +} + +static int ar934x_spi_transfer_one_message(struct spi_controller *master, + struct spi_message *m) +{ + struct ar934x_spi *sp = spi_controller_get_devdata(master); + struct spi_transfer *t = NULL; + struct spi_device *spi = m->spi; + unsigned long trx_done, trx_cur; + int stat = 0; + u8 term = 0; + int div, i; + u32 reg; + const u8 *tx_buf; + u8 *buf; + + m->actual_length = 0; + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->speed_hz) + div = ar934x_spi_clk_div(sp, t->speed_hz); + else + div = ar934x_spi_clk_div(sp, spi->max_speed_hz); + if (div < 0) { + stat = -EIO; + goto msg_done; + } + + reg = ioread32(sp->base + AR934X_SPI_REG_CTRL); + reg &= ~AR934X_SPI_CLK_MASK; + reg |= div; + iowrite32(reg, sp->base + AR934X_SPI_REG_CTRL); + iowrite32(0, sp->base + AR934X_SPI_DATAOUT); + + for (trx_done = 0; trx_done < t->len; trx_done += 4) { + trx_cur = t->len - trx_done; + if (trx_cur > 4) + trx_cur = 4; + else if (list_is_last(&t->transfer_list, &m->transfers)) + term = 1; + + if (t->tx_buf) { + tx_buf = t->tx_buf + trx_done; + reg = tx_buf[0]; + for (i = 1; i < trx_cur; i++) + reg = reg << 8 | tx_buf[i]; + iowrite32(reg, sp->base + AR934X_SPI_DATAOUT); + } + + reg = AR934X_SPI_SHIFT_VAL(spi->chip_select, term, + trx_cur * 8); + iowrite32(reg, sp->base + AR934X_SPI_REG_SHIFT_CTRL); + stat = readl_poll_timeout( + sp->base + AR934X_SPI_REG_SHIFT_CTRL, reg, + !(reg & AR934X_SPI_SHIFT_EN), 0, 5); + if (stat < 0) + goto msg_done; + + if (t->rx_buf) { + reg = ioread32(sp->base + AR934X_SPI_DATAIN); + buf = t->rx_buf + trx_done; + for (i = 0; i < trx_cur; i++) { + buf[trx_cur - i - 1] = reg & 0xff; + reg >>= 8; + } + } + } + m->actual_length += t->len; + } + +msg_done: + m->status = stat; + spi_finalize_current_message(master); + + return 0; +} + +static const struct of_device_id ar934x_spi_match[] = { + { .compatible = "qca,ar934x-spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ar934x_spi_match); + +static int ar934x_spi_probe(struct platform_device *pdev) +{ + struct spi_controller *ctlr; + struct ar934x_spi *sp; + void __iomem *base; + struct clk *clk; + int ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp)); + if (!ctlr) { + dev_info(&pdev->dev, "failed to allocate spi controller\n"); + return -ENOMEM; + } + + /* disable flash mapping and expose spi controller registers */ + iowrite32(AR934X_SPI_ENABLE, base + AR934X_SPI_REG_FS); + /* restore pins to default state: CSn=1 DO=CLK=0 */ + iowrite32(AR934X_SPI_IOC_INITVAL, base + AR934X_SPI_REG_IOC); + + ctlr->mode_bits = SPI_LSB_FIRST; + ctlr->setup = ar934x_spi_setup; + ctlr->transfer_one_message = ar934x_spi_transfer_one_message; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ctlr->dev.of_node = pdev->dev.of_node; + ctlr->num_chipselect = 3; + + dev_set_drvdata(&pdev->dev, ctlr); + + sp = spi_controller_get_devdata(ctlr); + sp->base = base; + sp->clk = clk; + sp->clk_freq = clk_get_rate(clk); + sp->ctlr = ctlr; + + return devm_spi_register_controller(&pdev->dev, ctlr); +} + +static int ar934x_spi_remove(struct platform_device *pdev) +{ + struct spi_controller *ctlr; + struct ar934x_spi *sp; + + ctlr = dev_get_drvdata(&pdev->dev); + sp = spi_controller_get_devdata(ctlr); + + clk_disable_unprepare(sp->clk); + + return 0; +} + +static struct platform_driver ar934x_spi_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = ar934x_spi_match, + }, + .probe = ar934x_spi_probe, + .remove = ar934x_spi_remove, +}; + +module_platform_driver(ar934x_spi_driver); + +MODULE_DESCRIPTION("SPI controller driver for Qualcomm Atheros AR934x/QCA95xx"); +MODULE_AUTHOR("Chuanhong Guo "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); From 78af6edf180d4f007561fba735d0c5dbd1739e90 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Mon, 10 Feb 2020 11:41:52 +0800 Subject: [PATCH 0071/2231] dt-binding: spi: add bindings for spi-ar934x Add binding documentation for SPI controller in Qualcomm Atheros AR934x/QCA95xx SoCs. Signed-off-by: Chuanhong Guo Link: https://lore.kernel.org/r/20200210034152.49063-3-gch981213@gmail.com Signed-off-by: Mark Brown --- .../bindings/spi/qca,ar934x-spi.yaml | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/qca,ar934x-spi.yaml diff --git a/Documentation/devicetree/bindings/spi/qca,ar934x-spi.yaml b/Documentation/devicetree/bindings/spi/qca,ar934x-spi.yaml new file mode 100644 index 000000000000..2aa766759d59 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/qca,ar934x-spi.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/qca,ar934x-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Atheros AR934x/QCA95xx SoC SPI controller + +maintainers: + - Chuanhong Guo + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: qca,ar934x-spi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - '#address-cells' + - '#size-cells' + +examples: + - | + #include + spi: spi@1f000000 { + compatible = "qca,ar934x-spi"; + reg = <0x1f000000 0x1c>; + clocks = <&pll ATH79_CLK_AHB>; + #address-cells = <1>; + #size-cells = <0>; + }; From b3b9c187dc2544923a601733a85352b9ddaba9b3 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Thu, 6 Feb 2020 10:24:03 -0500 Subject: [PATCH 0072/2231] locking/lockdep: Decrement IRQ context counters when removing lock chain There are currently three counters to track the IRQ context of a lock chain - nr_hardirq_chains, nr_softirq_chains and nr_process_chains. They are incremented when a new lock chain is added, but they are not decremented when a lock chain is removed. That causes some of the statistic counts reported by /proc/lockdep_stats to be incorrect. IRQ Fix that by decrementing the right counter when a lock chain is removed. Since inc_chains() no longer accesses hardirq_context and softirq_context directly, it is moved out from the CONFIG_TRACE_IRQFLAGS conditional compilation block. Fixes: a0b0fd53e1e6 ("locking/lockdep: Free lock classes that are no longer in use") Signed-off-by: Waiman Long Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200206152408.24165-2-longman@redhat.com --- kernel/locking/lockdep.c | 40 +++++++++++++++++------------- kernel/locking/lockdep_internals.h | 6 +++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 32406ef0d6a2..35449f5b79fb 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -2298,18 +2298,6 @@ static int check_irq_usage(struct task_struct *curr, struct held_lock *prev, return 0; } -static void inc_chains(void) -{ - if (current->hardirq_context) - nr_hardirq_chains++; - else { - if (current->softirq_context) - nr_softirq_chains++; - else - nr_process_chains++; - } -} - #else static inline int check_irq_usage(struct task_struct *curr, @@ -2317,13 +2305,27 @@ static inline int check_irq_usage(struct task_struct *curr, { return 1; } +#endif /* CONFIG_TRACE_IRQFLAGS */ -static inline void inc_chains(void) +static void inc_chains(int irq_context) { - nr_process_chains++; + if (irq_context & LOCK_CHAIN_HARDIRQ_CONTEXT) + nr_hardirq_chains++; + else if (irq_context & LOCK_CHAIN_SOFTIRQ_CONTEXT) + nr_softirq_chains++; + else + nr_process_chains++; } -#endif /* CONFIG_TRACE_IRQFLAGS */ +static void dec_chains(int irq_context) +{ + if (irq_context & LOCK_CHAIN_HARDIRQ_CONTEXT) + nr_hardirq_chains--; + else if (irq_context & LOCK_CHAIN_SOFTIRQ_CONTEXT) + nr_softirq_chains--; + else + nr_process_chains--; +} static void print_deadlock_scenario(struct held_lock *nxt, struct held_lock *prv) @@ -2843,7 +2845,7 @@ static inline int add_chain_cache(struct task_struct *curr, hlist_add_head_rcu(&chain->entry, hash_head); debug_atomic_inc(chain_lookup_misses); - inc_chains(); + inc_chains(chain->irq_context); return 1; } @@ -3596,7 +3598,8 @@ lock_used: static inline unsigned int task_irq_context(struct task_struct *task) { - return 2 * !!task->hardirq_context + !!task->softirq_context; + return LOCK_CHAIN_HARDIRQ_CONTEXT * !!task->hardirq_context + + LOCK_CHAIN_SOFTIRQ_CONTEXT * !!task->softirq_context; } static int separate_irq_context(struct task_struct *curr, @@ -4798,6 +4801,8 @@ recalc: return; /* Overwrite the chain key for concurrent RCU readers. */ WRITE_ONCE(chain->chain_key, chain_key); + dec_chains(chain->irq_context); + /* * Note: calling hlist_del_rcu() from inside a * hlist_for_each_entry_rcu() loop is safe. @@ -4819,6 +4824,7 @@ recalc: } *new_chain = *chain; hlist_add_head_rcu(&new_chain->entry, chainhashentry(chain_key)); + inc_chains(new_chain->irq_context); #endif } diff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h index 18d85aebbb57..a525368b8cf6 100644 --- a/kernel/locking/lockdep_internals.h +++ b/kernel/locking/lockdep_internals.h @@ -106,6 +106,12 @@ static const unsigned long LOCKF_USED_IN_IRQ_READ = #define STACK_TRACE_HASH_SIZE 16384 #endif +/* + * Bit definitions for lock_chain.irq_context + */ +#define LOCK_CHAIN_SOFTIRQ_CONTEXT (1 << 0) +#define LOCK_CHAIN_HARDIRQ_CONTEXT (1 << 1) + #define MAX_LOCKDEP_CHAINS (1UL << MAX_LOCKDEP_CHAINS_BITS) #define MAX_LOCKDEP_CHAIN_HLOCKS (MAX_LOCKDEP_CHAINS*5) From b9875e9882295749a14b31e16dd504ae904cf070 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Thu, 6 Feb 2020 10:24:04 -0500 Subject: [PATCH 0073/2231] locking/lockdep: Display irq_context names in /proc/lockdep_chains Currently, the irq_context field of a lock chains displayed in /proc/lockdep_chains is just a number. It is likely that many people may not know what a non-zero number means. To make the information more useful, print the actual irq names ("softirq" and "hardirq") instead. Signed-off-by: Waiman Long Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200206152408.24165-3-longman@redhat.com --- kernel/locking/lockdep_proc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c index 231684cfc5ae..c2224381c149 100644 --- a/kernel/locking/lockdep_proc.c +++ b/kernel/locking/lockdep_proc.c @@ -128,6 +128,13 @@ static int lc_show(struct seq_file *m, void *v) struct lock_chain *chain = v; struct lock_class *class; int i; + static const char * const irq_strs[] = { + [0] = "0", + [LOCK_CHAIN_HARDIRQ_CONTEXT] = "hardirq", + [LOCK_CHAIN_SOFTIRQ_CONTEXT] = "softirq", + [LOCK_CHAIN_SOFTIRQ_CONTEXT| + LOCK_CHAIN_HARDIRQ_CONTEXT] = "hardirq|softirq", + }; if (v == SEQ_START_TOKEN) { if (nr_chain_hlocks > MAX_LOCKDEP_CHAIN_HLOCKS) @@ -136,7 +143,7 @@ static int lc_show(struct seq_file *m, void *v) return 0; } - seq_printf(m, "irq_context: %d\n", chain->irq_context); + seq_printf(m, "irq_context: %s\n", irq_strs[chain->irq_context]); for (i = 0; i < chain->depth; i++) { class = lock_chain_get_class(chain, i); From 1d44bcb4fdb650b2a57c9ff593a4d246a10ad801 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Thu, 6 Feb 2020 10:24:05 -0500 Subject: [PATCH 0074/2231] locking/lockdep: Track number of zapped classes The whole point of the lockdep dynamic key patch is to allow unused locks to be removed from the lockdep data buffers so that existing buffer space can be reused. However, there is no way to find out how many unused locks are zapped and so we don't know if the zapping process is working properly. Add a new nr_zapped_classes counter to track that and show it in /proc/lockdep_stats. Signed-off-by: Waiman Long Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200206152408.24165-4-longman@redhat.com --- kernel/locking/lockdep.c | 2 ++ kernel/locking/lockdep_internals.h | 1 + kernel/locking/lockdep_proc.c | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 35449f5b79fb..28222d03345f 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -147,6 +147,7 @@ static DECLARE_BITMAP(list_entries_in_use, MAX_LOCKDEP_ENTRIES); #define KEYHASH_SIZE (1UL << KEYHASH_BITS) static struct hlist_head lock_keys_hash[KEYHASH_SIZE]; unsigned long nr_lock_classes; +unsigned long nr_zapped_classes; #ifndef CONFIG_DEBUG_LOCKDEP static #endif @@ -4880,6 +4881,7 @@ static void zap_class(struct pending_free *pf, struct lock_class *class) } remove_class_from_lock_chains(pf, class); + nr_zapped_classes++; } static void reinit_class(struct lock_class *class) diff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h index a525368b8cf6..76db80446a32 100644 --- a/kernel/locking/lockdep_internals.h +++ b/kernel/locking/lockdep_internals.h @@ -130,6 +130,7 @@ extern const char *__get_key_name(const struct lockdep_subclass_key *key, struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i); extern unsigned long nr_lock_classes; +extern unsigned long nr_zapped_classes; extern unsigned long nr_list_entries; long lockdep_next_lockchain(long i); unsigned long lock_chain_count(void); diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c index c2224381c149..99e2f3f6adca 100644 --- a/kernel/locking/lockdep_proc.c +++ b/kernel/locking/lockdep_proc.c @@ -343,6 +343,12 @@ static int lockdep_stats_show(struct seq_file *m, void *v) seq_printf(m, " debug_locks: %11u\n", debug_locks); + /* + * Zappped classes and lockdep data buffers reuse statistics. + */ + seq_puts(m, "\n"); + seq_printf(m, " zapped classes: %11lu\n", + nr_zapped_classes); return 0; } From 836bd74b5957779171c9648e9e29145fc089fffe Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Thu, 6 Feb 2020 10:24:06 -0500 Subject: [PATCH 0075/2231] locking/lockdep: Throw away all lock chains with zapped class If a lock chain contains a class that is zapped, the whole lock chain is likely to be invalid. If the zapped class is at the end of the chain, the partial chain without the zapped class should have been stored already as the current code will store all its predecessor chains. If the zapped class is somewhere in the middle, there is no guarantee that the partial chain will actually happen. It may just clutter up the hash and make searching slower. I would rather prefer storing the chain only when it actually happens. So just dump the corresponding chain_hlocks entries for now. A latter patch will try to reuse the freed chain_hlocks entries. This patch also changes the type of nr_chain_hlocks to unsigned integer to be consistent with the other counters. Signed-off-by: Waiman Long Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200206152408.24165-5-longman@redhat.com --- kernel/locking/lockdep.c | 37 ++++-------------------------- kernel/locking/lockdep_internals.h | 4 ++-- kernel/locking/lockdep_proc.c | 2 +- 3 files changed, 7 insertions(+), 36 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 28222d03345f..ef2a6432dd10 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -2625,8 +2625,8 @@ out_bug: struct lock_chain lock_chains[MAX_LOCKDEP_CHAINS]; static DECLARE_BITMAP(lock_chains_in_use, MAX_LOCKDEP_CHAINS); -int nr_chain_hlocks; static u16 chain_hlocks[MAX_LOCKDEP_CHAIN_HLOCKS]; +unsigned int nr_chain_hlocks; struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i) { @@ -4772,36 +4772,23 @@ static void remove_class_from_lock_chain(struct pending_free *pf, struct lock_class *class) { #ifdef CONFIG_PROVE_LOCKING - struct lock_chain *new_chain; - u64 chain_key; int i; for (i = chain->base; i < chain->base + chain->depth; i++) { if (chain_hlocks[i] != class - lock_classes) continue; - /* The code below leaks one chain_hlock[] entry. */ - if (--chain->depth > 0) { - memmove(&chain_hlocks[i], &chain_hlocks[i + 1], - (chain->base + chain->depth - i) * - sizeof(chain_hlocks[0])); - } /* * Each lock class occurs at most once in a lock chain so once * we found a match we can break out of this loop. */ - goto recalc; + goto free_lock_chain; } /* Since the chain has not been modified, return. */ return; -recalc: - chain_key = INITIAL_CHAIN_KEY; - for (i = chain->base; i < chain->base + chain->depth; i++) - chain_key = iterate_chain_key(chain_key, chain_hlocks[i]); - if (chain->depth && chain->chain_key == chain_key) - return; +free_lock_chain: /* Overwrite the chain key for concurrent RCU readers. */ - WRITE_ONCE(chain->chain_key, chain_key); + WRITE_ONCE(chain->chain_key, INITIAL_CHAIN_KEY); dec_chains(chain->irq_context); /* @@ -4810,22 +4797,6 @@ recalc: */ hlist_del_rcu(&chain->entry); __set_bit(chain - lock_chains, pf->lock_chains_being_freed); - if (chain->depth == 0) - return; - /* - * If the modified lock chain matches an existing lock chain, drop - * the modified lock chain. - */ - if (lookup_chain_cache(chain_key)) - return; - new_chain = alloc_lock_chain(); - if (WARN_ON_ONCE(!new_chain)) { - debug_locks_off(); - return; - } - *new_chain = *chain; - hlist_add_head_rcu(&new_chain->entry, chainhashentry(chain_key)); - inc_chains(new_chain->irq_context); #endif } diff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h index 76db80446a32..926bfa4b4564 100644 --- a/kernel/locking/lockdep_internals.h +++ b/kernel/locking/lockdep_internals.h @@ -134,14 +134,14 @@ extern unsigned long nr_zapped_classes; extern unsigned long nr_list_entries; long lockdep_next_lockchain(long i); unsigned long lock_chain_count(void); -extern int nr_chain_hlocks; extern unsigned long nr_stack_trace_entries; extern unsigned int nr_hardirq_chains; extern unsigned int nr_softirq_chains; extern unsigned int nr_process_chains; -extern unsigned int max_lockdep_depth; +extern unsigned int nr_chain_hlocks; +extern unsigned int max_lockdep_depth; extern unsigned int max_bfs_queue_depth; #ifdef CONFIG_PROVE_LOCKING diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c index 99e2f3f6adca..53c2a2ab4f07 100644 --- a/kernel/locking/lockdep_proc.c +++ b/kernel/locking/lockdep_proc.c @@ -278,7 +278,7 @@ static int lockdep_stats_show(struct seq_file *m, void *v) #ifdef CONFIG_PROVE_LOCKING seq_printf(m, " dependency chains: %11lu [max: %lu]\n", lock_chain_count(), MAX_LOCKDEP_CHAINS); - seq_printf(m, " dependency chain hlocks: %11d [max: %lu]\n", + seq_printf(m, " dependency chain hlocks: %11u [max: %lu]\n", nr_chain_hlocks, MAX_LOCKDEP_CHAIN_HLOCKS); #endif From 797b82eb906eeba24dcd6e9ab92faef01fc684cb Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Thu, 6 Feb 2020 10:24:07 -0500 Subject: [PATCH 0076/2231] locking/lockdep: Track number of zapped lock chains Add a new counter nr_zapped_lock_chains to track the number lock chains that have been removed. Signed-off-by: Waiman Long Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200206152408.24165-6-longman@redhat.com --- kernel/locking/lockdep.c | 2 ++ kernel/locking/lockdep_internals.h | 1 + kernel/locking/lockdep_proc.c | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index ef2a6432dd10..a63976c75253 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -2626,6 +2626,7 @@ out_bug: struct lock_chain lock_chains[MAX_LOCKDEP_CHAINS]; static DECLARE_BITMAP(lock_chains_in_use, MAX_LOCKDEP_CHAINS); static u16 chain_hlocks[MAX_LOCKDEP_CHAIN_HLOCKS]; +unsigned long nr_zapped_lock_chains; unsigned int nr_chain_hlocks; struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i) @@ -4797,6 +4798,7 @@ free_lock_chain: */ hlist_del_rcu(&chain->entry); __set_bit(chain - lock_chains, pf->lock_chains_being_freed); + nr_zapped_lock_chains++; #endif } diff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h index 926bfa4b4564..af722ceeda33 100644 --- a/kernel/locking/lockdep_internals.h +++ b/kernel/locking/lockdep_internals.h @@ -131,6 +131,7 @@ struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i); extern unsigned long nr_lock_classes; extern unsigned long nr_zapped_classes; +extern unsigned long nr_zapped_lock_chains; extern unsigned long nr_list_entries; long lockdep_next_lockchain(long i); unsigned long lock_chain_count(void); diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c index 53c2a2ab4f07..524580db4779 100644 --- a/kernel/locking/lockdep_proc.c +++ b/kernel/locking/lockdep_proc.c @@ -349,6 +349,10 @@ static int lockdep_stats_show(struct seq_file *m, void *v) seq_puts(m, "\n"); seq_printf(m, " zapped classes: %11lu\n", nr_zapped_classes); +#ifdef CONFIG_PROVE_LOCKING + seq_printf(m, " zapped lock chains: %11lu\n", + nr_zapped_lock_chains); +#endif return 0; } From 810507fe6fd5ff3de429121adff49523fabb643a Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Thu, 6 Feb 2020 10:24:08 -0500 Subject: [PATCH 0077/2231] locking/lockdep: Reuse freed chain_hlocks entries Once a lock class is zapped, all the lock chains that include the zapped class are essentially useless. The lock_chain structure itself can be reused, but not the corresponding chain_hlocks[] entries. Over time, we will run out of chain_hlocks entries while there are still plenty of other lockdep array entries available. To fix this imbalance, we have to make chain_hlocks entries reusable just like the others. As the freed chain_hlocks entries are in blocks of various lengths. A simple bitmap like the one used in the other reusable lockdep arrays isn't applicable. Instead the chain_hlocks entries are put into bucketed lists (MAX_CHAIN_BUCKETS) of chain blocks. Bucket 0 is the variable size bucket which houses chain blocks of size larger than MAX_CHAIN_BUCKETS sorted in decreasing size order. Initially, the whole array is in one chain block (the primordial chain block) in bucket 0. The minimum size of a chain block is 2 chain_hlocks entries. That will be the minimum allocation size. In other word, allocation requests for one chain_hlocks entry will cause 2-entry block to be returned and hence 1 entry will be wasted. Allocation requests for the chain_hlocks are fulfilled first by looking for chain block of matching size. If not found, the first chain block from bucket[0] (the largest one) is split. That can cause hlock entries fragmentation and reduce allocation efficiency if a chain block of size > MAX_CHAIN_BUCKETS is ever zapped and put back to after the primordial chain block. So the MAX_CHAIN_BUCKETS must be large enough that this should seldom happen. By reusing the chain_hlocks entries, we are able to handle workloads that add and zap a lot of lock classes without the risk of running out of chain_hlocks entries as long as the total number of outstanding lock classes at any time remain within a reasonable limit. Two new tracking counters, nr_free_chain_hlocks & nr_large_chain_blocks, are added to track the total number of chain_hlocks entries in the free bucketed lists and the number of large chain blocks in buckets[0] respectively. The nr_free_chain_hlocks replaces nr_chain_hlocks. The nr_large_chain_blocks counter enables to see if we should increase the number of buckets (MAX_CHAIN_BUCKETS) available so as to avoid to avoid the fragmentation problem in bucket[0]. An internal nfsd test that ran for more than an hour and kept on loading and unloading kernel modules could cause the following message to be displayed. [ 4318.443670] BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low! The patched kernel was able to complete the test with a lot of free chain_hlocks entries to spare: # cat /proc/lockdep_stats : dependency chains: 18867 [max: 65536] dependency chain hlocks: 74926 [max: 327680] dependency chain hlocks lost: 0 : zapped classes: 1541 zapped lock chains: 56765 large chain blocks: 1 By changing MAX_CHAIN_BUCKETS to 3 and add a counter for the size of the largest chain block. The system still worked and We got the following lockdep_stats data: dependency chains: 18601 [max: 65536] dependency chain hlocks used: 73133 [max: 327680] dependency chain hlocks lost: 0 : zapped classes: 1541 zapped lock chains: 56702 large chain blocks: 45165 large chain block size: 20165 By running the test again, I was indeed able to cause chain_hlocks entries to get lost: dependency chain hlocks used: 74806 [max: 327680] dependency chain hlocks lost: 575 : large chain blocks: 48737 large chain block size: 7 Due to the fragmentation, it is possible that the "MAX_LOCKDEP_CHAIN_HLOCKS too low!" error can happen even if a lot of of chain_hlocks entries appear to be free. Fortunately, a MAX_CHAIN_BUCKETS value of 16 should be big enough that few variable sized chain blocks, other than the initial one, should ever be present in bucket 0. Suggested-by: Peter Zijlstra Signed-off-by: Waiman Long Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200206152408.24165-7-longman@redhat.com --- kernel/locking/lockdep.c | 254 +++++++++++++++++++++++++++-- kernel/locking/lockdep_internals.h | 4 +- kernel/locking/lockdep_proc.c | 12 +- 3 files changed, 255 insertions(+), 15 deletions(-) diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index a63976c75253..e55c4ee14e64 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -1071,13 +1071,15 @@ static inline void check_data_structures(void) { } #endif /* CONFIG_DEBUG_LOCKDEP */ +static void init_chain_block_buckets(void); + /* * Initialize the lock_classes[] array elements, the free_lock_classes list * and also the delayed_free structure. */ static void init_data_structures_once(void) { - static bool ds_initialized, rcu_head_initialized; + static bool __read_mostly ds_initialized, rcu_head_initialized; int i; if (likely(rcu_head_initialized)) @@ -1101,6 +1103,7 @@ static void init_data_structures_once(void) INIT_LIST_HEAD(&lock_classes[i].locks_after); INIT_LIST_HEAD(&lock_classes[i].locks_before); } + init_chain_block_buckets(); } static inline struct hlist_head *keyhashentry(const struct lock_class_key *key) @@ -2627,7 +2630,233 @@ struct lock_chain lock_chains[MAX_LOCKDEP_CHAINS]; static DECLARE_BITMAP(lock_chains_in_use, MAX_LOCKDEP_CHAINS); static u16 chain_hlocks[MAX_LOCKDEP_CHAIN_HLOCKS]; unsigned long nr_zapped_lock_chains; -unsigned int nr_chain_hlocks; +unsigned int nr_free_chain_hlocks; /* Free chain_hlocks in buckets */ +unsigned int nr_lost_chain_hlocks; /* Lost chain_hlocks */ +unsigned int nr_large_chain_blocks; /* size > MAX_CHAIN_BUCKETS */ + +/* + * The first 2 chain_hlocks entries in the chain block in the bucket + * list contains the following meta data: + * + * entry[0]: + * Bit 15 - always set to 1 (it is not a class index) + * Bits 0-14 - upper 15 bits of the next block index + * entry[1] - lower 16 bits of next block index + * + * A next block index of all 1 bits means it is the end of the list. + * + * On the unsized bucket (bucket-0), the 3rd and 4th entries contain + * the chain block size: + * + * entry[2] - upper 16 bits of the chain block size + * entry[3] - lower 16 bits of the chain block size + */ +#define MAX_CHAIN_BUCKETS 16 +#define CHAIN_BLK_FLAG (1U << 15) +#define CHAIN_BLK_LIST_END 0xFFFFU + +static int chain_block_buckets[MAX_CHAIN_BUCKETS]; + +static inline int size_to_bucket(int size) +{ + if (size > MAX_CHAIN_BUCKETS) + return 0; + + return size - 1; +} + +/* + * Iterate all the chain blocks in a bucket. + */ +#define for_each_chain_block(bucket, prev, curr) \ + for ((prev) = -1, (curr) = chain_block_buckets[bucket]; \ + (curr) >= 0; \ + (prev) = (curr), (curr) = chain_block_next(curr)) + +/* + * next block or -1 + */ +static inline int chain_block_next(int offset) +{ + int next = chain_hlocks[offset]; + + WARN_ON_ONCE(!(next & CHAIN_BLK_FLAG)); + + if (next == CHAIN_BLK_LIST_END) + return -1; + + next &= ~CHAIN_BLK_FLAG; + next <<= 16; + next |= chain_hlocks[offset + 1]; + + return next; +} + +/* + * bucket-0 only + */ +static inline int chain_block_size(int offset) +{ + return (chain_hlocks[offset + 2] << 16) | chain_hlocks[offset + 3]; +} + +static inline void init_chain_block(int offset, int next, int bucket, int size) +{ + chain_hlocks[offset] = (next >> 16) | CHAIN_BLK_FLAG; + chain_hlocks[offset + 1] = (u16)next; + + if (size && !bucket) { + chain_hlocks[offset + 2] = size >> 16; + chain_hlocks[offset + 3] = (u16)size; + } +} + +static inline void add_chain_block(int offset, int size) +{ + int bucket = size_to_bucket(size); + int next = chain_block_buckets[bucket]; + int prev, curr; + + if (unlikely(size < 2)) { + /* + * We can't store single entries on the freelist. Leak them. + * + * One possible way out would be to uniquely mark them, other + * than with CHAIN_BLK_FLAG, such that we can recover them when + * the block before it is re-added. + */ + if (size) + nr_lost_chain_hlocks++; + return; + } + + nr_free_chain_hlocks += size; + if (!bucket) { + nr_large_chain_blocks++; + + /* + * Variable sized, sort large to small. + */ + for_each_chain_block(0, prev, curr) { + if (size >= chain_block_size(curr)) + break; + } + init_chain_block(offset, curr, 0, size); + if (prev < 0) + chain_block_buckets[0] = offset; + else + init_chain_block(prev, offset, 0, 0); + return; + } + /* + * Fixed size, add to head. + */ + init_chain_block(offset, next, bucket, size); + chain_block_buckets[bucket] = offset; +} + +/* + * Only the first block in the list can be deleted. + * + * For the variable size bucket[0], the first block (the largest one) is + * returned, broken up and put back into the pool. So if a chain block of + * length > MAX_CHAIN_BUCKETS is ever used and zapped, it will just be + * queued up after the primordial chain block and never be used until the + * hlock entries in the primordial chain block is almost used up. That + * causes fragmentation and reduce allocation efficiency. That can be + * monitored by looking at the "large chain blocks" number in lockdep_stats. + */ +static inline void del_chain_block(int bucket, int size, int next) +{ + nr_free_chain_hlocks -= size; + chain_block_buckets[bucket] = next; + + if (!bucket) + nr_large_chain_blocks--; +} + +static void init_chain_block_buckets(void) +{ + int i; + + for (i = 0; i < MAX_CHAIN_BUCKETS; i++) + chain_block_buckets[i] = -1; + + add_chain_block(0, ARRAY_SIZE(chain_hlocks)); +} + +/* + * Return offset of a chain block of the right size or -1 if not found. + * + * Fairly simple worst-fit allocator with the addition of a number of size + * specific free lists. + */ +static int alloc_chain_hlocks(int req) +{ + int bucket, curr, size; + + /* + * We rely on the MSB to act as an escape bit to denote freelist + * pointers. Make sure this bit isn't set in 'normal' class_idx usage. + */ + BUILD_BUG_ON((MAX_LOCKDEP_KEYS-1) & CHAIN_BLK_FLAG); + + init_data_structures_once(); + + if (nr_free_chain_hlocks < req) + return -1; + + /* + * We require a minimum of 2 (u16) entries to encode a freelist + * 'pointer'. + */ + req = max(req, 2); + bucket = size_to_bucket(req); + curr = chain_block_buckets[bucket]; + + if (bucket) { + if (curr >= 0) { + del_chain_block(bucket, req, chain_block_next(curr)); + return curr; + } + /* Try bucket 0 */ + curr = chain_block_buckets[0]; + } + + /* + * The variable sized freelist is sorted by size; the first entry is + * the largest. Use it if it fits. + */ + if (curr >= 0) { + size = chain_block_size(curr); + if (likely(size >= req)) { + del_chain_block(0, size, chain_block_next(curr)); + add_chain_block(curr + req, size - req); + return curr; + } + } + + /* + * Last resort, split a block in a larger sized bucket. + */ + for (size = MAX_CHAIN_BUCKETS; size > req; size--) { + bucket = size_to_bucket(size); + curr = chain_block_buckets[bucket]; + if (curr < 0) + continue; + + del_chain_block(bucket, size, chain_block_next(curr)); + add_chain_block(curr + req, size - req); + return curr; + } + + return -1; +} + +static inline void free_chain_hlocks(int base, int size) +{ + add_chain_block(base, max(size, 2)); +} struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i) { @@ -2828,15 +3057,8 @@ static inline int add_chain_cache(struct task_struct *curr, BUILD_BUG_ON((1UL << 6) <= ARRAY_SIZE(curr->held_locks)); BUILD_BUG_ON((1UL << 8*sizeof(chain_hlocks[0])) <= ARRAY_SIZE(lock_classes)); - if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) { - chain->base = nr_chain_hlocks; - for (j = 0; j < chain->depth - 1; j++, i++) { - int lock_id = curr->held_locks[i].class_idx; - chain_hlocks[chain->base + j] = lock_id; - } - chain_hlocks[chain->base + j] = class - lock_classes; - nr_chain_hlocks += chain->depth; - } else { + j = alloc_chain_hlocks(chain->depth); + if (j < 0) { if (!debug_locks_off_graph_unlock()) return 0; @@ -2845,6 +3067,13 @@ static inline int add_chain_cache(struct task_struct *curr, return 0; } + chain->base = j; + for (j = 0; j < chain->depth - 1; j++, i++) { + int lock_id = curr->held_locks[i].class_idx; + + chain_hlocks[chain->base + j] = lock_id; + } + chain_hlocks[chain->base + j] = class - lock_classes; hlist_add_head_rcu(&chain->entry, hash_head); debug_atomic_inc(chain_lookup_misses); inc_chains(chain->irq_context); @@ -2991,6 +3220,8 @@ static inline int validate_chain(struct task_struct *curr, { return 1; } + +static void init_chain_block_buckets(void) { } #endif /* CONFIG_PROVE_LOCKING */ /* @@ -4788,6 +5019,7 @@ static void remove_class_from_lock_chain(struct pending_free *pf, return; free_lock_chain: + free_chain_hlocks(chain->base, chain->depth); /* Overwrite the chain key for concurrent RCU readers. */ WRITE_ONCE(chain->chain_key, INITIAL_CHAIN_KEY); dec_chains(chain->irq_context); diff --git a/kernel/locking/lockdep_internals.h b/kernel/locking/lockdep_internals.h index af722ceeda33..baca699b94e9 100644 --- a/kernel/locking/lockdep_internals.h +++ b/kernel/locking/lockdep_internals.h @@ -140,7 +140,9 @@ extern unsigned long nr_stack_trace_entries; extern unsigned int nr_hardirq_chains; extern unsigned int nr_softirq_chains; extern unsigned int nr_process_chains; -extern unsigned int nr_chain_hlocks; +extern unsigned int nr_free_chain_hlocks; +extern unsigned int nr_lost_chain_hlocks; +extern unsigned int nr_large_chain_blocks; extern unsigned int max_lockdep_depth; extern unsigned int max_bfs_queue_depth; diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c index 524580db4779..5525cd3ba0c8 100644 --- a/kernel/locking/lockdep_proc.c +++ b/kernel/locking/lockdep_proc.c @@ -137,7 +137,7 @@ static int lc_show(struct seq_file *m, void *v) }; if (v == SEQ_START_TOKEN) { - if (nr_chain_hlocks > MAX_LOCKDEP_CHAIN_HLOCKS) + if (!nr_free_chain_hlocks) seq_printf(m, "(buggered) "); seq_printf(m, "all lock chains:\n"); return 0; @@ -278,8 +278,12 @@ static int lockdep_stats_show(struct seq_file *m, void *v) #ifdef CONFIG_PROVE_LOCKING seq_printf(m, " dependency chains: %11lu [max: %lu]\n", lock_chain_count(), MAX_LOCKDEP_CHAINS); - seq_printf(m, " dependency chain hlocks: %11u [max: %lu]\n", - nr_chain_hlocks, MAX_LOCKDEP_CHAIN_HLOCKS); + seq_printf(m, " dependency chain hlocks used: %11lu [max: %lu]\n", + MAX_LOCKDEP_CHAIN_HLOCKS - + (nr_free_chain_hlocks + nr_lost_chain_hlocks), + MAX_LOCKDEP_CHAIN_HLOCKS); + seq_printf(m, " dependency chain hlocks lost: %11u\n", + nr_lost_chain_hlocks); #endif #ifdef CONFIG_TRACE_IRQFLAGS @@ -352,6 +356,8 @@ static int lockdep_stats_show(struct seq_file *m, void *v) #ifdef CONFIG_PROVE_LOCKING seq_printf(m, " zapped lock chains: %11lu\n", nr_zapped_lock_chains); + seq_printf(m, " large chain blocks: %11u\n", + nr_large_chain_blocks); #endif return 0; } From 1751060e2527462714359573a39dca10451ffbf8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2019 20:01:26 +0100 Subject: [PATCH 0078/2231] locking/percpu-rwsem, lockdep: Make percpu-rwsem use its own lockdep_map As preparation for replacing the embedded rwsem, give percpu-rwsem its own lockdep_map. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200131151539.927625541@infradead.org --- include/linux/percpu-rwsem.h | 29 +++++++++++++++++++---------- kernel/cpu.c | 4 ++-- kernel/locking/percpu-rwsem.c | 16 ++++++++++++---- kernel/locking/rwsem.c | 4 ++-- kernel/locking/rwsem.h | 2 ++ 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index ad2ca2a89d5b..f2c36fb5e661 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -15,8 +15,17 @@ struct percpu_rw_semaphore { struct rw_semaphore rw_sem; /* slowpath */ struct rcuwait writer; /* blocked writer */ int readers_block; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif }; +#ifdef CONFIG_DEBUG_LOCK_ALLOC +#define __PERCPU_RWSEM_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname }, +#else +#define __PERCPU_RWSEM_DEP_MAP_INIT(lockname) +#endif + #define __DEFINE_PERCPU_RWSEM(name, is_static) \ static DEFINE_PER_CPU(unsigned int, __percpu_rwsem_rc_##name); \ is_static struct percpu_rw_semaphore name = { \ @@ -24,7 +33,9 @@ is_static struct percpu_rw_semaphore name = { \ .read_count = &__percpu_rwsem_rc_##name, \ .rw_sem = __RWSEM_INITIALIZER(name.rw_sem), \ .writer = __RCUWAIT_INITIALIZER(name.writer), \ + __PERCPU_RWSEM_DEP_MAP_INIT(name) \ } + #define DEFINE_PERCPU_RWSEM(name) \ __DEFINE_PERCPU_RWSEM(name, /* not static */) #define DEFINE_STATIC_PERCPU_RWSEM(name) \ @@ -37,7 +48,7 @@ static inline void percpu_down_read(struct percpu_rw_semaphore *sem) { might_sleep(); - rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 0, _RET_IP_); + rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_); preempt_disable(); /* @@ -76,13 +87,15 @@ static inline int percpu_down_read_trylock(struct percpu_rw_semaphore *sem) */ if (ret) - rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 1, _RET_IP_); + rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_); return ret; } static inline void percpu_up_read(struct percpu_rw_semaphore *sem) { + rwsem_release(&sem->dep_map, _RET_IP_); + preempt_disable(); /* * Same as in percpu_down_read(). @@ -92,8 +105,6 @@ static inline void percpu_up_read(struct percpu_rw_semaphore *sem) else __percpu_up_read(sem); /* Unconditional memory barrier */ preempt_enable(); - - rwsem_release(&sem->rw_sem.dep_map, _RET_IP_); } extern void percpu_down_write(struct percpu_rw_semaphore *); @@ -110,15 +121,13 @@ extern void percpu_free_rwsem(struct percpu_rw_semaphore *); __percpu_init_rwsem(sem, #sem, &rwsem_key); \ }) -#define percpu_rwsem_is_held(sem) lockdep_is_held(&(sem)->rw_sem) - -#define percpu_rwsem_assert_held(sem) \ - lockdep_assert_held(&(sem)->rw_sem) +#define percpu_rwsem_is_held(sem) lockdep_is_held(sem) +#define percpu_rwsem_assert_held(sem) lockdep_assert_held(sem) static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem, bool read, unsigned long ip) { - lock_release(&sem->rw_sem.dep_map, ip); + lock_release(&sem->dep_map, ip); #ifdef CONFIG_RWSEM_SPIN_ON_OWNER if (!read) atomic_long_set(&sem->rw_sem.owner, RWSEM_OWNER_UNKNOWN); @@ -128,7 +137,7 @@ static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem, static inline void percpu_rwsem_acquire(struct percpu_rw_semaphore *sem, bool read, unsigned long ip) { - lock_acquire(&sem->rw_sem.dep_map, 0, 1, read, 1, NULL, ip); + lock_acquire(&sem->dep_map, 0, 1, read, 1, NULL, ip); #ifdef CONFIG_RWSEM_SPIN_ON_OWNER if (!read) atomic_long_set(&sem->rw_sem.owner, (long)current); diff --git a/kernel/cpu.c b/kernel/cpu.c index 9c706af713fb..221bf6a9e98a 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -331,12 +331,12 @@ void lockdep_assert_cpus_held(void) static void lockdep_acquire_cpus_lock(void) { - rwsem_acquire(&cpu_hotplug_lock.rw_sem.dep_map, 0, 0, _THIS_IP_); + rwsem_acquire(&cpu_hotplug_lock.dep_map, 0, 0, _THIS_IP_); } static void lockdep_release_cpus_lock(void) { - rwsem_release(&cpu_hotplug_lock.rw_sem.dep_map, _THIS_IP_); + rwsem_release(&cpu_hotplug_lock.dep_map, _THIS_IP_); } /* diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index 364d38a0c444..aa2b118d2f88 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -11,7 +11,7 @@ #include "rwsem.h" int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, - const char *name, struct lock_class_key *rwsem_key) + const char *name, struct lock_class_key *key) { sem->read_count = alloc_percpu(int); if (unlikely(!sem->read_count)) @@ -19,9 +19,13 @@ int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, /* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */ rcu_sync_init(&sem->rss); - __init_rwsem(&sem->rw_sem, name, rwsem_key); + init_rwsem(&sem->rw_sem); rcuwait_init(&sem->writer); sem->readers_block = 0; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + debug_check_no_locks_freed((void *)sem, sizeof(*sem)); + lockdep_init_map(&sem->dep_map, name, key, 0); +#endif return 0; } EXPORT_SYMBOL_GPL(__percpu_init_rwsem); @@ -142,10 +146,12 @@ static bool readers_active_check(struct percpu_rw_semaphore *sem) void percpu_down_write(struct percpu_rw_semaphore *sem) { + rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_); + /* Notify readers to take the slow path. */ rcu_sync_enter(&sem->rss); - down_write(&sem->rw_sem); + __down_write(&sem->rw_sem); /* * Notify new readers to block; up until now, and thus throughout the @@ -168,6 +174,8 @@ EXPORT_SYMBOL_GPL(percpu_down_write); void percpu_up_write(struct percpu_rw_semaphore *sem) { + rwsem_release(&sem->dep_map, _RET_IP_); + /* * Signal the writer is done, no fast path yet. * @@ -183,7 +191,7 @@ void percpu_up_write(struct percpu_rw_semaphore *sem) /* * Release the write lock, this will allow readers back in the game. */ - up_write(&sem->rw_sem); + __up_write(&sem->rw_sem); /* * Once this completes (at least one RCU-sched grace period hence) the diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 0d9b6be9ecc8..30df8dff217b 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -1383,7 +1383,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem) /* * lock for writing */ -static inline void __down_write(struct rw_semaphore *sem) +inline void __down_write(struct rw_semaphore *sem) { long tmp = RWSEM_UNLOCKED_VALUE; @@ -1446,7 +1446,7 @@ inline void __up_read(struct rw_semaphore *sem) /* * unlock after writing */ -static inline void __up_write(struct rw_semaphore *sem) +inline void __up_write(struct rw_semaphore *sem) { long tmp; diff --git a/kernel/locking/rwsem.h b/kernel/locking/rwsem.h index 2534ce49f648..d0d33a59622d 100644 --- a/kernel/locking/rwsem.h +++ b/kernel/locking/rwsem.h @@ -6,5 +6,7 @@ extern void __down_read(struct rw_semaphore *sem); extern void __up_read(struct rw_semaphore *sem); +extern void __down_write(struct rw_semaphore *sem); +extern void __up_write(struct rw_semaphore *sem); #endif /* __INTERNAL_RWSEM_H */ From 206c98ffbeda588dbbd9d272505c42acbc364a30 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2019 20:12:37 +0100 Subject: [PATCH 0079/2231] locking/percpu-rwsem: Convert to bool Use bool where possible. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200131151539.984626569@infradead.org --- include/linux/percpu-rwsem.h | 6 +++--- kernel/locking/percpu-rwsem.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index f2c36fb5e661..4ceaa1921951 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -41,7 +41,7 @@ is_static struct percpu_rw_semaphore name = { \ #define DEFINE_STATIC_PERCPU_RWSEM(name) \ __DEFINE_PERCPU_RWSEM(name, static) -extern int __percpu_down_read(struct percpu_rw_semaphore *, int); +extern bool __percpu_down_read(struct percpu_rw_semaphore *, bool); extern void __percpu_up_read(struct percpu_rw_semaphore *); static inline void percpu_down_read(struct percpu_rw_semaphore *sem) @@ -69,9 +69,9 @@ static inline void percpu_down_read(struct percpu_rw_semaphore *sem) preempt_enable(); } -static inline int percpu_down_read_trylock(struct percpu_rw_semaphore *sem) +static inline bool percpu_down_read_trylock(struct percpu_rw_semaphore *sem) { - int ret = 1; + bool ret = true; preempt_disable(); /* diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index aa2b118d2f88..969389df6eee 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -45,7 +45,7 @@ void percpu_free_rwsem(struct percpu_rw_semaphore *sem) } EXPORT_SYMBOL_GPL(percpu_free_rwsem); -int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) +bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) { /* * Due to having preemption disabled the decrement happens on @@ -69,7 +69,7 @@ int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) * release in percpu_up_write(). */ if (likely(!smp_load_acquire(&sem->readers_block))) - return 1; + return true; /* * Per the above comment; we still have preemption disabled and @@ -78,7 +78,7 @@ int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) __percpu_up_read(sem); if (try) - return 0; + return false; /* * We either call schedule() in the wait, or we'll fall through @@ -94,7 +94,7 @@ int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) __up_read(&sem->rw_sem); preempt_disable(); - return 1; + return true; } EXPORT_SYMBOL_GPL(__percpu_down_read); From 71365d40232110f7b029befc9033ea311d680611 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2019 20:17:51 +0100 Subject: [PATCH 0080/2231] locking/percpu-rwsem: Move __this_cpu_inc() into the slowpath As preparation to rework __percpu_down_read() move the __this_cpu_inc() into it. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200131151540.041600199@infradead.org --- include/linux/percpu-rwsem.h | 10 ++++++---- kernel/locking/percpu-rwsem.c | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index 4ceaa1921951..bb5b71c1e446 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -59,8 +59,9 @@ static inline void percpu_down_read(struct percpu_rw_semaphore *sem) * and that once the synchronize_rcu() is done, the writer will see * anything we did within this RCU-sched read-size critical section. */ - __this_cpu_inc(*sem->read_count); - if (unlikely(!rcu_sync_is_idle(&sem->rss))) + if (likely(rcu_sync_is_idle(&sem->rss))) + __this_cpu_inc(*sem->read_count); + else __percpu_down_read(sem, false); /* Unconditional memory barrier */ /* * The preempt_enable() prevents the compiler from @@ -77,8 +78,9 @@ static inline bool percpu_down_read_trylock(struct percpu_rw_semaphore *sem) /* * Same as in percpu_down_read(). */ - __this_cpu_inc(*sem->read_count); - if (unlikely(!rcu_sync_is_idle(&sem->rss))) + if (likely(rcu_sync_is_idle(&sem->rss))) + __this_cpu_inc(*sem->read_count); + else ret = __percpu_down_read(sem, true); /* Unconditional memory barrier */ preempt_enable(); /* diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index 969389df6eee..becf925b27b5 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -47,6 +47,8 @@ EXPORT_SYMBOL_GPL(percpu_free_rwsem); bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) { + __this_cpu_inc(*sem->read_count); + /* * Due to having preemption disabled the decrement happens on * the same CPU as the increment, avoiding the From 75ff64572e497578e238fefbdff221c96f29067a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Oct 2019 12:34:23 +0100 Subject: [PATCH 0081/2231] locking/percpu-rwsem: Extract __percpu_down_read_trylock() In preparation for removing the embedded rwsem and building a custom lock, extract the read-trylock primitive. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200131151540.098485539@infradead.org --- kernel/locking/percpu-rwsem.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index becf925b27b5..b155e8e7ac39 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -45,7 +45,7 @@ void percpu_free_rwsem(struct percpu_rw_semaphore *sem) } EXPORT_SYMBOL_GPL(percpu_free_rwsem); -bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) +static bool __percpu_down_read_trylock(struct percpu_rw_semaphore *sem) { __this_cpu_inc(*sem->read_count); @@ -73,11 +73,18 @@ bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) if (likely(!smp_load_acquire(&sem->readers_block))) return true; - /* - * Per the above comment; we still have preemption disabled and - * will thus decrement on the same CPU as we incremented. - */ - __percpu_up_read(sem); + __this_cpu_dec(*sem->read_count); + + /* Prod writer to re-evaluate readers_active_check() */ + rcuwait_wake_up(&sem->writer); + + return false; +} + +bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) +{ + if (__percpu_down_read_trylock(sem)) + return true; if (try) return false; From 7f26482a872c36b2ee87ea95b9dcd96e3d5805df Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2019 20:30:41 +0100 Subject: [PATCH 0082/2231] locking/percpu-rwsem: Remove the embedded rwsem The filesystem freezer uses percpu-rwsem in a way that is effectively write_non_owner() and achieves this with a few horrible hacks that rely on the rwsem (!percpu) implementation. When PREEMPT_RT replaces the rwsem implementation with a PI aware variant this comes apart. Remove the embedded rwsem and implement it using a waitqueue and an atomic_t. - make readers_block an atomic, and use it, with the waitqueue for a blocking test-and-set write-side. - have the read-side wait for the 'lock' state to clear. Have the waiters use FIFO queueing and mark them (reader/writer) with a new WQ_FLAG. Use a custom wake_function to wake either a single writer or all readers until a writer. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200204092403.GB14879@hirez.programming.kicks-ass.net --- include/linux/percpu-rwsem.h | 19 ++-- include/linux/wait.h | 1 + kernel/locking/percpu-rwsem.c | 159 ++++++++++++++++++++++++---------- kernel/locking/rwsem.c | 9 +- kernel/locking/rwsem.h | 12 --- 5 files changed, 126 insertions(+), 74 deletions(-) diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index bb5b71c1e446..f5ecf6a8a1dd 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -3,18 +3,18 @@ #define _LINUX_PERCPU_RWSEM_H #include -#include #include #include +#include #include #include struct percpu_rw_semaphore { struct rcu_sync rss; unsigned int __percpu *read_count; - struct rw_semaphore rw_sem; /* slowpath */ - struct rcuwait writer; /* blocked writer */ - int readers_block; + struct rcuwait writer; + wait_queue_head_t waiters; + atomic_t block; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif @@ -31,8 +31,9 @@ static DEFINE_PER_CPU(unsigned int, __percpu_rwsem_rc_##name); \ is_static struct percpu_rw_semaphore name = { \ .rss = __RCU_SYNC_INITIALIZER(name.rss), \ .read_count = &__percpu_rwsem_rc_##name, \ - .rw_sem = __RWSEM_INITIALIZER(name.rw_sem), \ .writer = __RCUWAIT_INITIALIZER(name.writer), \ + .waiters = __WAIT_QUEUE_HEAD_INITIALIZER(name.waiters), \ + .block = ATOMIC_INIT(0), \ __PERCPU_RWSEM_DEP_MAP_INIT(name) \ } @@ -130,20 +131,12 @@ static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem, bool read, unsigned long ip) { lock_release(&sem->dep_map, ip); -#ifdef CONFIG_RWSEM_SPIN_ON_OWNER - if (!read) - atomic_long_set(&sem->rw_sem.owner, RWSEM_OWNER_UNKNOWN); -#endif } static inline void percpu_rwsem_acquire(struct percpu_rw_semaphore *sem, bool read, unsigned long ip) { lock_acquire(&sem->dep_map, 0, 1, read, 1, NULL, ip); -#ifdef CONFIG_RWSEM_SPIN_ON_OWNER - if (!read) - atomic_long_set(&sem->rw_sem.owner, (long)current); -#endif } #endif diff --git a/include/linux/wait.h b/include/linux/wait.h index 3283c8d02137..feeb6be5cad6 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -20,6 +20,7 @@ int default_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int #define WQ_FLAG_EXCLUSIVE 0x01 #define WQ_FLAG_WOKEN 0x02 #define WQ_FLAG_BOOKMARK 0x04 +#define WQ_FLAG_CUSTOM 0x08 /* * A single wait-queue entry structure: diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index b155e8e7ac39..a136677543b4 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -1,15 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include #include +#include #include #include #include #include +#include #include -#include "rwsem.h" - int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, const char *name, struct lock_class_key *key) { @@ -17,11 +16,10 @@ int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, if (unlikely(!sem->read_count)) return -ENOMEM; - /* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */ rcu_sync_init(&sem->rss); - init_rwsem(&sem->rw_sem); rcuwait_init(&sem->writer); - sem->readers_block = 0; + init_waitqueue_head(&sem->waiters); + atomic_set(&sem->block, 0); #ifdef CONFIG_DEBUG_LOCK_ALLOC debug_check_no_locks_freed((void *)sem, sizeof(*sem)); lockdep_init_map(&sem->dep_map, name, key, 0); @@ -54,23 +52,23 @@ static bool __percpu_down_read_trylock(struct percpu_rw_semaphore *sem) * the same CPU as the increment, avoiding the * increment-on-one-CPU-and-decrement-on-another problem. * - * If the reader misses the writer's assignment of readers_block, then - * the writer is guaranteed to see the reader's increment. + * If the reader misses the writer's assignment of sem->block, then the + * writer is guaranteed to see the reader's increment. * * Conversely, any readers that increment their sem->read_count after - * the writer looks are guaranteed to see the readers_block value, - * which in turn means that they are guaranteed to immediately - * decrement their sem->read_count, so that it doesn't matter that the - * writer missed them. + * the writer looks are guaranteed to see the sem->block value, which + * in turn means that they are guaranteed to immediately decrement + * their sem->read_count, so that it doesn't matter that the writer + * missed them. */ smp_mb(); /* A matches D */ /* - * If !readers_block the critical section starts here, matched by the + * If !sem->block the critical section starts here, matched by the * release in percpu_up_write(). */ - if (likely(!smp_load_acquire(&sem->readers_block))) + if (likely(!atomic_read_acquire(&sem->block))) return true; __this_cpu_dec(*sem->read_count); @@ -81,6 +79,88 @@ static bool __percpu_down_read_trylock(struct percpu_rw_semaphore *sem) return false; } +static inline bool __percpu_down_write_trylock(struct percpu_rw_semaphore *sem) +{ + if (atomic_read(&sem->block)) + return false; + + return atomic_xchg(&sem->block, 1) == 0; +} + +static bool __percpu_rwsem_trylock(struct percpu_rw_semaphore *sem, bool reader) +{ + if (reader) { + bool ret; + + preempt_disable(); + ret = __percpu_down_read_trylock(sem); + preempt_enable(); + + return ret; + } + return __percpu_down_write_trylock(sem); +} + +/* + * The return value of wait_queue_entry::func means: + * + * <0 - error, wakeup is terminated and the error is returned + * 0 - no wakeup, a next waiter is tried + * >0 - woken, if EXCLUSIVE, counted towards @nr_exclusive. + * + * We use EXCLUSIVE for both readers and writers to preserve FIFO order, + * and play games with the return value to allow waking multiple readers. + * + * Specifically, we wake readers until we've woken a single writer, or until a + * trylock fails. + */ +static int percpu_rwsem_wake_function(struct wait_queue_entry *wq_entry, + unsigned int mode, int wake_flags, + void *key) +{ + struct task_struct *p = get_task_struct(wq_entry->private); + bool reader = wq_entry->flags & WQ_FLAG_CUSTOM; + struct percpu_rw_semaphore *sem = key; + + /* concurrent against percpu_down_write(), can get stolen */ + if (!__percpu_rwsem_trylock(sem, reader)) + return 1; + + list_del_init(&wq_entry->entry); + smp_store_release(&wq_entry->private, NULL); + + wake_up_process(p); + put_task_struct(p); + + return !reader; /* wake (readers until) 1 writer */ +} + +static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader) +{ + DEFINE_WAIT_FUNC(wq_entry, percpu_rwsem_wake_function); + bool wait; + + spin_lock_irq(&sem->waiters.lock); + /* + * Serialize against the wakeup in percpu_up_write(), if we fail + * the trylock, the wakeup must see us on the list. + */ + wait = !__percpu_rwsem_trylock(sem, reader); + if (wait) { + wq_entry.flags |= WQ_FLAG_EXCLUSIVE | reader * WQ_FLAG_CUSTOM; + __add_wait_queue_entry_tail(&sem->waiters, &wq_entry); + } + spin_unlock_irq(&sem->waiters.lock); + + while (wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!smp_load_acquire(&wq_entry.private)) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); +} + bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) { if (__percpu_down_read_trylock(sem)) @@ -89,20 +169,10 @@ bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) if (try) return false; - /* - * We either call schedule() in the wait, or we'll fall through - * and reschedule on the preempt_enable() in percpu_down_read(). - */ - preempt_enable_no_resched(); - - /* - * Avoid lockdep for the down/up_read() we already have them. - */ - __down_read(&sem->rw_sem); - this_cpu_inc(*sem->read_count); - __up_read(&sem->rw_sem); - + preempt_enable(); + percpu_rwsem_wait(sem, /* .reader = */ true); preempt_disable(); + return true; } EXPORT_SYMBOL_GPL(__percpu_down_read); @@ -117,7 +187,7 @@ void __percpu_up_read(struct percpu_rw_semaphore *sem) */ __this_cpu_dec(*sem->read_count); - /* Prod writer to recheck readers_active */ + /* Prod writer to re-evaluate readers_active_check() */ rcuwait_wake_up(&sem->writer); } EXPORT_SYMBOL_GPL(__percpu_up_read); @@ -137,6 +207,8 @@ EXPORT_SYMBOL_GPL(__percpu_up_read); * zero. If this sum is zero, then it is stable due to the fact that if any * newly arriving readers increment a given counter, they will immediately * decrement that same counter. + * + * Assumes sem->block is set. */ static bool readers_active_check(struct percpu_rw_semaphore *sem) { @@ -160,23 +232,22 @@ void percpu_down_write(struct percpu_rw_semaphore *sem) /* Notify readers to take the slow path. */ rcu_sync_enter(&sem->rss); - __down_write(&sem->rw_sem); + /* + * Try set sem->block; this provides writer-writer exclusion. + * Having sem->block set makes new readers block. + */ + if (!__percpu_down_write_trylock(sem)) + percpu_rwsem_wait(sem, /* .reader = */ false); + + /* smp_mb() implied by __percpu_down_write_trylock() on success -- D matches A */ /* - * Notify new readers to block; up until now, and thus throughout the - * longish rcu_sync_enter() above, new readers could still come in. - */ - WRITE_ONCE(sem->readers_block, 1); - - smp_mb(); /* D matches A */ - - /* - * If they don't see our writer of readers_block, then we are - * guaranteed to see their sem->read_count increment, and therefore - * will wait for them. + * If they don't see our store of sem->block, then we are guaranteed to + * see their sem->read_count increment, and therefore will wait for + * them. */ - /* Wait for all now active readers to complete. */ + /* Wait for all active readers to complete. */ rcuwait_wait_event(&sem->writer, readers_active_check(sem)); } EXPORT_SYMBOL_GPL(percpu_down_write); @@ -195,12 +266,12 @@ void percpu_up_write(struct percpu_rw_semaphore *sem) * Therefore we force it through the slow path which guarantees an * acquire and thereby guarantees the critical section's consistency. */ - smp_store_release(&sem->readers_block, 0); + atomic_set_release(&sem->block, 0); /* - * Release the write lock, this will allow readers back in the game. + * Prod any pending reader/writer to make progress. */ - __up_write(&sem->rw_sem); + __wake_up(&sem->waiters, TASK_NORMAL, 1, sem); /* * Once this completes (at least one RCU-sched grace period hence) the diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 30df8dff217b..81c0d75d5dab 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -28,7 +28,6 @@ #include #include -#include "rwsem.h" #include "lock_events.h" /* @@ -1338,7 +1337,7 @@ static struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem) /* * lock for reading */ -inline void __down_read(struct rw_semaphore *sem) +static inline void __down_read(struct rw_semaphore *sem) { if (!rwsem_read_trylock(sem)) { rwsem_down_read_slowpath(sem, TASK_UNINTERRUPTIBLE); @@ -1383,7 +1382,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem) /* * lock for writing */ -inline void __down_write(struct rw_semaphore *sem) +static inline void __down_write(struct rw_semaphore *sem) { long tmp = RWSEM_UNLOCKED_VALUE; @@ -1426,7 +1425,7 @@ static inline int __down_write_trylock(struct rw_semaphore *sem) /* * unlock after reading */ -inline void __up_read(struct rw_semaphore *sem) +static inline void __up_read(struct rw_semaphore *sem) { long tmp; @@ -1446,7 +1445,7 @@ inline void __up_read(struct rw_semaphore *sem) /* * unlock after writing */ -inline void __up_write(struct rw_semaphore *sem) +static inline void __up_write(struct rw_semaphore *sem) { long tmp; diff --git a/kernel/locking/rwsem.h b/kernel/locking/rwsem.h index d0d33a59622d..e69de29bb2d1 100644 --- a/kernel/locking/rwsem.h +++ b/kernel/locking/rwsem.h @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef __INTERNAL_RWSEM_H -#define __INTERNAL_RWSEM_H -#include - -extern void __down_read(struct rw_semaphore *sem); -extern void __up_read(struct rw_semaphore *sem); -extern void __down_write(struct rw_semaphore *sem); -extern void __up_write(struct rw_semaphore *sem); - -#endif /* __INTERNAL_RWSEM_H */ From bcba67cd806800fa8e973ac49dbc7d2d8fb3e55e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 4 Feb 2020 09:34:37 +0100 Subject: [PATCH 0083/2231] locking/rwsem: Remove RWSEM_OWNER_UNKNOWN Remove the now unused RWSEM_OWNER_UNKNOWN hack. This hack breaks PREEMPT_RT and getting rid of it was the entire motivation for re-writing the percpu rwsem. The biggest problem is that it is fundamentally incompatible with any form of Priority Inheritance, any exclusively held lock must have a distinct owner. Requested-by: Christoph Hellwig Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200204092228.GP14946@hirez.programming.kicks-ass.net --- include/linux/rwsem.h | 6 ------ kernel/locking/rwsem.c | 2 -- 2 files changed, 8 deletions(-) diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 00d6054687dd..8a418d9eeb7a 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -53,12 +53,6 @@ struct rw_semaphore { #endif }; -/* - * Setting all bits of the owner field except bit 0 will indicate - * that the rwsem is writer-owned with an unknown owner. - */ -#define RWSEM_OWNER_UNKNOWN (-2L) - /* In all implementations count != 0 means locked */ static inline int rwsem_is_locked(struct rw_semaphore *sem) { diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 81c0d75d5dab..e6f437bafb23 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -659,8 +659,6 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem, unsigned long flags; bool ret = true; - BUILD_BUG_ON(!(RWSEM_OWNER_UNKNOWN & RWSEM_NONSPINNABLE)); - if (need_resched()) { lockevent_inc(rwsem_opt_fail); return false; From ac8dec420970f5cbaf2f6eda39153a60ec5b257b Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 18 Nov 2019 15:19:35 -0800 Subject: [PATCH 0084/2231] locking/percpu-rwsem: Fold __percpu_up_read() Now that __percpu_up_read() is only ever used from percpu_up_read() merge them, it's a small function. Signed-off-by: Davidlohr Bueso Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Will Deacon Acked-by: Waiman Long Link: https://lkml.kernel.org/r/20200131151540.212415454@infradead.org --- include/linux/percpu-rwsem.h | 19 +++++++++++++++---- kernel/exit.c | 1 + kernel/locking/percpu-rwsem.c | 15 --------------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index f5ecf6a8a1dd..5e033fe1ff4e 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -43,7 +43,6 @@ is_static struct percpu_rw_semaphore name = { \ __DEFINE_PERCPU_RWSEM(name, static) extern bool __percpu_down_read(struct percpu_rw_semaphore *, bool); -extern void __percpu_up_read(struct percpu_rw_semaphore *); static inline void percpu_down_read(struct percpu_rw_semaphore *sem) { @@ -103,10 +102,22 @@ static inline void percpu_up_read(struct percpu_rw_semaphore *sem) /* * Same as in percpu_down_read(). */ - if (likely(rcu_sync_is_idle(&sem->rss))) + if (likely(rcu_sync_is_idle(&sem->rss))) { __this_cpu_dec(*sem->read_count); - else - __percpu_up_read(sem); /* Unconditional memory barrier */ + } else { + /* + * slowpath; reader will only ever wake a single blocked + * writer. + */ + smp_mb(); /* B matches C */ + /* + * In other words, if they see our decrement (presumably to + * aggregate zero, as that is the only time it matters) they + * will also see our critical section. + */ + __this_cpu_dec(*sem->read_count); + rcuwait_wake_up(&sem->writer); + } preempt_enable(); } diff --git a/kernel/exit.c b/kernel/exit.c index 2833ffb0c211..f64a8f9d412a 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -258,6 +258,7 @@ void rcuwait_wake_up(struct rcuwait *w) wake_up_process(task); rcu_read_unlock(); } +EXPORT_SYMBOL_GPL(rcuwait_wake_up); /* * Determine if a process group is "orphaned", according to the POSIX diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index a136677543b4..8048a9a255d5 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -177,21 +177,6 @@ bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) } EXPORT_SYMBOL_GPL(__percpu_down_read); -void __percpu_up_read(struct percpu_rw_semaphore *sem) -{ - smp_mb(); /* B matches C */ - /* - * In other words, if they see our decrement (presumably to aggregate - * zero, as that is the only time it matters) they will also see our - * critical section. - */ - __this_cpu_dec(*sem->read_count); - - /* Prod writer to re-evaluate readers_active_check() */ - rcuwait_wake_up(&sem->writer); -} -EXPORT_SYMBOL_GPL(__percpu_up_read); - #define per_cpu_sum(var) \ ({ \ typeof(var) __sum = 0; \ From 41f0e29190ac9e38099a37abd1a8a4cb1dc21233 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Tue, 7 Jan 2020 17:33:04 -0800 Subject: [PATCH 0085/2231] locking/percpu-rwsem: Add might_sleep() for writer locking We are missing this annotation in percpu_down_write(). Correct this. Signed-off-by: Davidlohr Bueso Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Will Deacon Acked-by: Waiman Long Link: https://lkml.kernel.org/r/20200108013305.7732-1-dave@stgolabs.net --- kernel/locking/percpu-rwsem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index 8048a9a255d5..183a3aaa8509 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -212,6 +212,7 @@ static bool readers_active_check(struct percpu_rw_semaphore *sem) void percpu_down_write(struct percpu_rw_semaphore *sem) { + might_sleep(); rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_); /* Notify readers to take the slow path. */ From 6c1c07b33eb093e5a2a313ece89baa596ba6135e Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 21 Jan 2020 10:13:38 -0800 Subject: [PATCH 0086/2231] perf/x86/intel: Avoid unnecessary PEBS_ENABLE MSR access in PMI The perf PMI handler, intel_pmu_handle_irq(), currently does unnecessary MSR accesses for PEBS_ENABLE MSR in __intel_pmu_enable/disable_all() when PEBS is enabled. When entering the handler, global ctrl is explicitly disabled. All counters do not count anymore. It doesn't matter if PEBS is enabled or not in a PMI handler. Furthermore, for most cases, the cpuc->pebs_enabled is not changed in PMI. The PEBS status doesn't change. The PEBS_ENABLE MSR doesn't need to be changed either when exiting the handler. PMI throttle may change the PEBS status during PMI handler. The x86_pmu_stop() ends up in intel_pmu_pebs_disable() which can update cpuc->pebs_enabled. But the MSR_IA32_PEBS_ENABLE is not updated at the same time. Because the cpuc->enabled has been forced to 0. The patch explicitly update the MSR_IA32_PEBS_ENABLE for this case. Use ftrace to measure the duration of intel_pmu_handle_irq() on BDX. #perf record -e cycles:P -- ./tchain_edit The average duration of intel_pmu_handle_irq(): Without the patch 1.144 us With the patch 1.025 us Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200121181338.3234-1-kan.liang@linux.intel.com --- arch/x86/events/intel/core.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index dff6623804c2..332954cccece 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -1945,6 +1945,14 @@ static __initconst const u64 knl_hw_cache_extra_regs * intel_bts events don't coexist with intel PMU's BTS events because of * x86_add_exclusive(x86_lbr_exclusive_lbr); there's no need to keep them * disabled around intel PMU's event batching etc, only inside the PMI handler. + * + * Avoid PEBS_ENABLE MSR access in PMIs. + * The GLOBAL_CTRL has been disabled. All the counters do not count anymore. + * It doesn't matter if the PEBS is enabled or not. + * Usually, the PEBS status are not changed in PMIs. It's unnecessary to + * access PEBS_ENABLE MSR in disable_all()/enable_all(). + * However, there are some cases which may change PEBS status, e.g. PMI + * throttle. The PEBS_ENABLE should be updated where the status changes. */ static void __intel_pmu_disable_all(void) { @@ -1954,13 +1962,12 @@ static void __intel_pmu_disable_all(void) if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) intel_pmu_disable_bts(); - - intel_pmu_pebs_disable_all(); } static void intel_pmu_disable_all(void) { __intel_pmu_disable_all(); + intel_pmu_pebs_disable_all(); intel_pmu_lbr_disable_all(); } @@ -1968,7 +1975,6 @@ static void __intel_pmu_enable_all(int added, bool pmi) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - intel_pmu_pebs_enable_all(); intel_pmu_lbr_enable_all(pmi); wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask); @@ -1986,6 +1992,7 @@ static void __intel_pmu_enable_all(int added, bool pmi) static void intel_pmu_enable_all(int added) { + intel_pmu_pebs_enable_all(); __intel_pmu_enable_all(added, false); } @@ -2374,9 +2381,21 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status) * PEBS overflow sets bit 62 in the global status register */ if (__test_and_clear_bit(62, (unsigned long *)&status)) { + u64 pebs_enabled = cpuc->pebs_enabled; + handled++; x86_pmu.drain_pebs(regs); status &= x86_pmu.intel_ctrl | GLOBAL_STATUS_TRACE_TOPAPMI; + + /* + * PMI throttle may be triggered, which stops the PEBS event. + * Although cpuc->pebs_enabled is updated accordingly, the + * MSR_IA32_PEBS_ENABLE is not updated. Because the + * cpuc->enabled has been forced to 0 in PMI. + * Update the MSR if pebs_enabled is changed. + */ + if (pebs_enabled != cpuc->pebs_enabled) + wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); } /* From bbfd5e4fab63703375eafaf241a0c696024a59e1 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Mon, 27 Jan 2020 08:53:54 -0800 Subject: [PATCH 0087/2231] perf/core: Add new branch sample type for HW index of raw branch records The low level index is the index in the underlying hardware buffer of the most recently captured taken branch which is always saved in branch_entries[0]. It is very useful for reconstructing the call stack. For example, in Intel LBR call stack mode, the depth of reconstructed LBR call stack limits to the number of LBR registers. With the low level index information, perf tool may stitch the stacks of two samples. The reconstructed LBR call stack can break the HW limitation. Add a new branch sample type to retrieve low level index of raw branch records. The low level index is between -1 (unknown) and max depth which can be retrieved in /sys/devices/cpu/caps/branches. Only when the new branch sample type is set, the low level index information is dumped into the PERF_SAMPLE_BRANCH_STACK output. Perf tool should check the attr.branch_sample_type, and apply the corresponding format for PERF_SAMPLE_BRANCH_STACK samples. Otherwise, some user case may be broken. For example, users may parse a perf.data, which include the new branch sample type, with an old version perf tool (without the check). Users probably get incorrect information without any warning. Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200127165355.27495-2-kan.liang@linux.intel.com --- arch/powerpc/perf/core-book3s.c | 1 + arch/x86/events/intel/lbr.c | 3 +++ include/linux/perf_event.h | 12 ++++++++++++ include/uapi/linux/perf_event.h | 8 +++++++- kernel/events/core.c | 10 ++++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 3086055bf681..3dcfecf858f3 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -518,6 +518,7 @@ static void power_pmu_bhrb_read(struct perf_event *event, struct cpu_hw_events * } } cpuhw->bhrb_stack.nr = u_index; + cpuhw->bhrb_stack.hw_idx = -1ULL; return; } diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c index 534c76606049..7639e2097101 100644 --- a/arch/x86/events/intel/lbr.c +++ b/arch/x86/events/intel/lbr.c @@ -585,6 +585,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) cpuc->lbr_entries[i].reserved = 0; } cpuc->lbr_stack.nr = i; + cpuc->lbr_stack.hw_idx = -1ULL; } /* @@ -680,6 +681,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) out++; } cpuc->lbr_stack.nr = out; + cpuc->lbr_stack.hw_idx = -1ULL; } void intel_pmu_lbr_read(void) @@ -1120,6 +1122,7 @@ void intel_pmu_store_pebs_lbrs(struct pebs_lbr *lbr) int i; cpuc->lbr_stack.nr = x86_pmu.lbr_nr; + cpuc->lbr_stack.hw_idx = -1ULL; for (i = 0; i < x86_pmu.lbr_nr; i++) { u64 info = lbr->lbr[i].info; struct perf_branch_entry *e = &cpuc->lbr_entries[i]; diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 547773f5894e..68e21e828893 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -93,14 +93,26 @@ struct perf_raw_record { /* * branch stack layout: * nr: number of taken branches stored in entries[] + * hw_idx: The low level index of raw branch records + * for the most recent branch. + * -1ULL means invalid/unknown. * * Note that nr can vary from sample to sample * branches (to, from) are stored from most recent * to least recent, i.e., entries[0] contains the most * recent branch. + * The entries[] is an abstraction of raw branch records, + * which may not be stored in age order in HW, e.g. Intel LBR. + * The hw_idx is to expose the low level index of raw + * branch record for the most recent branch aka entries[0]. + * The hw_idx index is between -1 (unknown) and max depth, + * which can be retrieved in /sys/devices/cpu/caps/branches. + * For the architectures whose raw branch records are + * already stored in age order, the hw_idx should be 0. */ struct perf_branch_stack { __u64 nr; + __u64 hw_idx; struct perf_branch_entry entries[0]; }; diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 377d794d3105..397cfd65b3fe 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -181,6 +181,8 @@ enum perf_branch_sample_type_shift { PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, /* save branch type */ + PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, /* save low level index of raw branch records */ + PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */ }; @@ -208,6 +210,8 @@ enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_TYPE_SAVE = 1U << PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT, + PERF_SAMPLE_BRANCH_HW_INDEX = 1U << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT, + PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT, }; @@ -853,7 +857,9 @@ enum perf_event_type { * char data[size];}&& PERF_SAMPLE_RAW * * { u64 nr; - * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK + * { u64 hw_idx; } && PERF_SAMPLE_BRANCH_HW_INDEX + * { u64 from, to, flags } lbr[nr]; + * } && PERF_SAMPLE_BRANCH_STACK * * { u64 abi; # enum perf_sample_regs_abi * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER diff --git a/kernel/events/core.c b/kernel/events/core.c index e453589da97c..3f1f77de7247 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6555,6 +6555,11 @@ static void perf_output_read(struct perf_output_handle *handle, perf_output_read_one(handle, event, enabled, running); } +static inline bool perf_sample_save_hw_index(struct perf_event *event) +{ + return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX; +} + void perf_output_sample(struct perf_output_handle *handle, struct perf_event_header *header, struct perf_sample_data *data, @@ -6643,6 +6648,8 @@ void perf_output_sample(struct perf_output_handle *handle, * sizeof(struct perf_branch_entry); perf_output_put(handle, data->br_stack->nr); + if (perf_sample_save_hw_index(event)) + perf_output_put(handle, data->br_stack->hw_idx); perf_output_copy(handle, data->br_stack->entries, size); } else { /* @@ -6836,6 +6843,9 @@ void perf_prepare_sample(struct perf_event_header *header, if (sample_type & PERF_SAMPLE_BRANCH_STACK) { int size = sizeof(u64); /* nr */ if (data->br_stack) { + if (perf_sample_save_hw_index(event)) + size += sizeof(u64); + size += data->br_stack->nr * sizeof(struct perf_branch_entry); } From db278b90c326ce5895be09b6171f5ff3df1e3cca Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Mon, 27 Jan 2020 08:53:55 -0800 Subject: [PATCH 0088/2231] perf/x86/intel: Output LBR TOS information correctly For Intel LBR, the LBR Top-of-Stack (TOS) information is the HW index of raw branch record for the most recent branch. For non-adaptive PEBS and non-PEBS, the TOS information can be directly retrieved from TOS MSR read in intel_pmu_lbr_read(). For adaptive PEBS, the LBR information stored in PEBS record doesn't include the TOS information. For single PEBS, TOS can be directly read from MSR, because the PMI is triggered immediately after PEBS is written. TOS MSR is still unchanged. For large PEBS, TOS MSR has stale value. Set -1ULL to indicate that the TOS information is not available. Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200127165355.27495-3-kan.liang@linux.intel.com --- arch/x86/events/intel/lbr.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c index 7639e2097101..65113b16804a 100644 --- a/arch/x86/events/intel/lbr.c +++ b/arch/x86/events/intel/lbr.c @@ -585,7 +585,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) cpuc->lbr_entries[i].reserved = 0; } cpuc->lbr_stack.nr = i; - cpuc->lbr_stack.hw_idx = -1ULL; + cpuc->lbr_stack.hw_idx = tos; } /* @@ -681,7 +681,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) out++; } cpuc->lbr_stack.nr = out; - cpuc->lbr_stack.hw_idx = -1ULL; + cpuc->lbr_stack.hw_idx = tos; } void intel_pmu_lbr_read(void) @@ -1122,7 +1122,13 @@ void intel_pmu_store_pebs_lbrs(struct pebs_lbr *lbr) int i; cpuc->lbr_stack.nr = x86_pmu.lbr_nr; - cpuc->lbr_stack.hw_idx = -1ULL; + + /* Cannot get TOS for large PEBS */ + if (cpuc->n_pebs == cpuc->n_large_pebs) + cpuc->lbr_stack.hw_idx = -1ULL; + else + cpuc->lbr_stack.hw_idx = intel_pmu_lbr_tos(); + for (i = 0; i < x86_pmu.lbr_nr; i++) { u64 info = lbr->lbr[i].info; struct perf_branch_entry *e = &cpuc->lbr_entries[i]; From fdb64822443ec9fb8c3a74b598a74790ae8d2e22 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 6 Feb 2020 08:15:27 -0800 Subject: [PATCH 0089/2231] perf/x86: Add Intel Tiger Lake uncore support For MSR type of uncore units, there is no difference between Ice Lake and Tiger Lake. Share the same code with Ice Lake. Tiger Lake has two MCs. Both of them are located at 0:0:0. The BAR offset is still 0x48. The offset of the two MCs is 0x10000. Each MC has three counters to count every read/write/total issued by the Memory Controller to DRAM. The counters can be accessed by MMIO. They are free-running counters. The offset of counters are different for TIGERLAKE_L and TIGERLAKE. Add separated mmio_init() functions. Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Andi Kleen Link: https://lkml.kernel.org/r/20200206161527.3529-1-kan.liang@linux.intel.com --- arch/x86/events/intel/uncore.c | 12 +++ arch/x86/events/intel/uncore.h | 2 + arch/x86/events/intel/uncore_snb.c | 159 +++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c index 86467f85c383..63922e3a34f5 100644 --- a/arch/x86/events/intel/uncore.c +++ b/arch/x86/events/intel/uncore.c @@ -1470,6 +1470,16 @@ static const struct intel_uncore_init_fun icl_uncore_init __initconst = { .pci_init = skl_uncore_pci_init, }; +static const struct intel_uncore_init_fun tgl_uncore_init __initconst = { + .cpu_init = icl_uncore_cpu_init, + .mmio_init = tgl_uncore_mmio_init, +}; + +static const struct intel_uncore_init_fun tgl_l_uncore_init __initconst = { + .cpu_init = icl_uncore_cpu_init, + .mmio_init = tgl_l_uncore_mmio_init, +}; + static const struct intel_uncore_init_fun snr_uncore_init __initconst = { .cpu_init = snr_uncore_cpu_init, .pci_init = snr_uncore_pci_init, @@ -1505,6 +1515,8 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = { X86_UNCORE_MODEL_MATCH(INTEL_FAM6_ICELAKE_L, icl_uncore_init), X86_UNCORE_MODEL_MATCH(INTEL_FAM6_ICELAKE_NNPI, icl_uncore_init), X86_UNCORE_MODEL_MATCH(INTEL_FAM6_ICELAKE, icl_uncore_init), + X86_UNCORE_MODEL_MATCH(INTEL_FAM6_TIGERLAKE_L, tgl_l_uncore_init), + X86_UNCORE_MODEL_MATCH(INTEL_FAM6_TIGERLAKE, tgl_uncore_init), X86_UNCORE_MODEL_MATCH(INTEL_FAM6_ATOM_TREMONT_D, snr_uncore_init), {}, }; diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h index bbfdaa720b45..1204dcc9fe9b 100644 --- a/arch/x86/events/intel/uncore.h +++ b/arch/x86/events/intel/uncore.h @@ -527,6 +527,8 @@ void snb_uncore_cpu_init(void); void nhm_uncore_cpu_init(void); void skl_uncore_cpu_init(void); void icl_uncore_cpu_init(void); +void tgl_uncore_mmio_init(void); +void tgl_l_uncore_mmio_init(void); int snb_pci2phy_map_init(int devid); /* uncore_snbep.c */ diff --git a/arch/x86/events/intel/uncore_snb.c b/arch/x86/events/intel/uncore_snb.c index c37cb12d0ef6..3de1065eefc4 100644 --- a/arch/x86/events/intel/uncore_snb.c +++ b/arch/x86/events/intel/uncore_snb.c @@ -44,6 +44,11 @@ #define PCI_DEVICE_ID_INTEL_WHL_UD_IMC 0x3e35 #define PCI_DEVICE_ID_INTEL_ICL_U_IMC 0x8a02 #define PCI_DEVICE_ID_INTEL_ICL_U2_IMC 0x8a12 +#define PCI_DEVICE_ID_INTEL_TGL_U1_IMC 0x9a02 +#define PCI_DEVICE_ID_INTEL_TGL_U2_IMC 0x9a04 +#define PCI_DEVICE_ID_INTEL_TGL_U3_IMC 0x9a12 +#define PCI_DEVICE_ID_INTEL_TGL_U4_IMC 0x9a14 +#define PCI_DEVICE_ID_INTEL_TGL_H_IMC 0x9a36 /* SNB event control */ @@ -1002,3 +1007,157 @@ void nhm_uncore_cpu_init(void) } /* end of Nehalem uncore support */ + +/* Tiger Lake MMIO uncore support */ + +static const struct pci_device_id tgl_uncore_pci_ids[] = { + { /* IMC */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGL_U1_IMC), + .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0), + }, + { /* IMC */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGL_U2_IMC), + .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0), + }, + { /* IMC */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGL_U3_IMC), + .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0), + }, + { /* IMC */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGL_U4_IMC), + .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0), + }, + { /* IMC */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGL_H_IMC), + .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0), + }, + { /* end: all zeroes */ } +}; + +enum perf_tgl_uncore_imc_freerunning_types { + TGL_MMIO_UNCORE_IMC_DATA_TOTAL, + TGL_MMIO_UNCORE_IMC_DATA_READ, + TGL_MMIO_UNCORE_IMC_DATA_WRITE, + TGL_MMIO_UNCORE_IMC_FREERUNNING_TYPE_MAX +}; + +static struct freerunning_counters tgl_l_uncore_imc_freerunning[] = { + [TGL_MMIO_UNCORE_IMC_DATA_TOTAL] = { 0x5040, 0x0, 0x0, 1, 64 }, + [TGL_MMIO_UNCORE_IMC_DATA_READ] = { 0x5058, 0x0, 0x0, 1, 64 }, + [TGL_MMIO_UNCORE_IMC_DATA_WRITE] = { 0x50A0, 0x0, 0x0, 1, 64 }, +}; + +static struct freerunning_counters tgl_uncore_imc_freerunning[] = { + [TGL_MMIO_UNCORE_IMC_DATA_TOTAL] = { 0xd840, 0x0, 0x0, 1, 64 }, + [TGL_MMIO_UNCORE_IMC_DATA_READ] = { 0xd858, 0x0, 0x0, 1, 64 }, + [TGL_MMIO_UNCORE_IMC_DATA_WRITE] = { 0xd8A0, 0x0, 0x0, 1, 64 }, +}; + +static struct uncore_event_desc tgl_uncore_imc_events[] = { + INTEL_UNCORE_EVENT_DESC(data_total, "event=0xff,umask=0x10"), + INTEL_UNCORE_EVENT_DESC(data_total.scale, "6.103515625e-5"), + INTEL_UNCORE_EVENT_DESC(data_total.unit, "MiB"), + + INTEL_UNCORE_EVENT_DESC(data_read, "event=0xff,umask=0x20"), + INTEL_UNCORE_EVENT_DESC(data_read.scale, "6.103515625e-5"), + INTEL_UNCORE_EVENT_DESC(data_read.unit, "MiB"), + + INTEL_UNCORE_EVENT_DESC(data_write, "event=0xff,umask=0x30"), + INTEL_UNCORE_EVENT_DESC(data_write.scale, "6.103515625e-5"), + INTEL_UNCORE_EVENT_DESC(data_write.unit, "MiB"), + + { /* end: all zeroes */ } +}; + +static struct pci_dev *tgl_uncore_get_mc_dev(void) +{ + const struct pci_device_id *ids = tgl_uncore_pci_ids; + struct pci_dev *mc_dev = NULL; + + while (ids && ids->vendor) { + mc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, ids->device, NULL); + if (mc_dev) + return mc_dev; + ids++; + } + + return mc_dev; +} + +#define TGL_UNCORE_MMIO_IMC_MEM_OFFSET 0x10000 + +static void tgl_uncore_imc_freerunning_init_box(struct intel_uncore_box *box) +{ + struct pci_dev *pdev = tgl_uncore_get_mc_dev(); + struct intel_uncore_pmu *pmu = box->pmu; + resource_size_t addr; + u32 mch_bar; + + if (!pdev) { + pr_warn("perf uncore: Cannot find matched IMC device.\n"); + return; + } + + pci_read_config_dword(pdev, SNB_UNCORE_PCI_IMC_BAR_OFFSET, &mch_bar); + /* MCHBAR is disabled */ + if (!(mch_bar & BIT(0))) { + pr_warn("perf uncore: MCHBAR is disabled. Failed to map IMC free-running counters.\n"); + return; + } + mch_bar &= ~BIT(0); + addr = (resource_size_t)(mch_bar + TGL_UNCORE_MMIO_IMC_MEM_OFFSET * pmu->pmu_idx); + +#ifdef CONFIG_PHYS_ADDR_T_64BIT + pci_read_config_dword(pdev, SNB_UNCORE_PCI_IMC_BAR_OFFSET + 4, &mch_bar); + addr |= ((resource_size_t)mch_bar << 32); +#endif + + box->io_addr = ioremap(addr, SNB_UNCORE_PCI_IMC_MAP_SIZE); +} + +static struct intel_uncore_ops tgl_uncore_imc_freerunning_ops = { + .init_box = tgl_uncore_imc_freerunning_init_box, + .exit_box = uncore_mmio_exit_box, + .read_counter = uncore_mmio_read_counter, + .hw_config = uncore_freerunning_hw_config, +}; + +static struct attribute *tgl_uncore_imc_formats_attr[] = { + &format_attr_event.attr, + &format_attr_umask.attr, + NULL +}; + +static const struct attribute_group tgl_uncore_imc_format_group = { + .name = "format", + .attrs = tgl_uncore_imc_formats_attr, +}; + +static struct intel_uncore_type tgl_uncore_imc_free_running = { + .name = "imc_free_running", + .num_counters = 3, + .num_boxes = 2, + .num_freerunning_types = TGL_MMIO_UNCORE_IMC_FREERUNNING_TYPE_MAX, + .freerunning = tgl_uncore_imc_freerunning, + .ops = &tgl_uncore_imc_freerunning_ops, + .event_descs = tgl_uncore_imc_events, + .format_group = &tgl_uncore_imc_format_group, +}; + +static struct intel_uncore_type *tgl_mmio_uncores[] = { + &tgl_uncore_imc_free_running, + NULL +}; + +void tgl_l_uncore_mmio_init(void) +{ + tgl_uncore_imc_free_running.freerunning = tgl_l_uncore_imc_freerunning; + uncore_mmio_uncores = tgl_mmio_uncores; +} + +void tgl_uncore_mmio_init(void) +{ + uncore_mmio_uncores = tgl_mmio_uncores; +} + +/* end of Tiger Lake MMIO uncore support */ From 644592d328370af4b3e027b7b1ae9f81613782d8 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 10 Feb 2020 12:32:38 -0600 Subject: [PATCH 0090/2231] objtool: Fail the kernel build on fatal errors When objtool encounters a fatal error, it usually means the binary is corrupt or otherwise broken in some way. Up until now, such errors were just treated as warnings which didn't fail the kernel build. However, objtool is now stable enough that if a fatal error is discovered, it most likely means something is seriously wrong and it should fail the kernel build. Note that this doesn't apply to "normal" objtool warnings; only fatal ones. Suggested-by: Borislav Petkov Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Reviewed-by: Julien Thierry Link: https://lkml.kernel.org/r/f18c3743de0fef673d49dd35760f26bdef7f6fc3.1581359535.git.jpoimboe@redhat.com --- tools/objtool/check.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4768d91c6d68..796f6a172efd 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2491,8 +2491,14 @@ int check(const char *_objname, bool orc) out: cleanup(&file); - /* ignore warnings for now until we get all the code cleaned up */ - if (ret || warnings) - return 0; + if (ret < 0) { + /* + * Fatal error. The binary is corrupt or otherwise broken in + * some way, or objtool itself is broken. Fail the kernel + * build. + */ + return ret; + } + return 0; } From a22961409c02b93ffa7ed78f67fb57a1ba6c787d Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 10 Feb 2020 12:32:39 -0600 Subject: [PATCH 0091/2231] objtool: Add is_static_jump() helper There are several places where objtool tests for a non-dynamic (aka direct) jump. Move the check to a helper function. Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Reviewed-by: Julien Thierry Link: https://lkml.kernel.org/r/9b8b438df918276315e4765c60d2587f3c7ad698.1581359535.git.jpoimboe@redhat.com --- tools/objtool/check.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 796f6a172efd..9016ae1c0c55 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -97,14 +97,19 @@ static struct instruction *next_insn_same_func(struct objtool_file *file, for (insn = next_insn_same_sec(file, insn); insn; \ insn = next_insn_same_sec(file, insn)) +static bool is_static_jump(struct instruction *insn) +{ + return insn->type == INSN_JUMP_CONDITIONAL || + insn->type == INSN_JUMP_UNCONDITIONAL; +} + static bool is_sibling_call(struct instruction *insn) { /* An indirect jump is either a sibling call or a jump to a table. */ if (insn->type == INSN_JUMP_DYNAMIC) return list_empty(&insn->alts); - if (insn->type != INSN_JUMP_CONDITIONAL && - insn->type != INSN_JUMP_UNCONDITIONAL) + if (!is_static_jump(insn)) return false; /* add_jump_destinations() sets insn->call_dest for sibling calls. */ @@ -553,8 +558,7 @@ static int add_jump_destinations(struct objtool_file *file) unsigned long dest_off; for_each_insn(file, insn) { - if (insn->type != INSN_JUMP_CONDITIONAL && - insn->type != INSN_JUMP_UNCONDITIONAL) + if (!is_static_jump(insn)) continue; if (insn->ignore || insn->offset == FAKE_JUMP_OFFSET) @@ -764,8 +768,7 @@ static int handle_group_alt(struct objtool_file *file, insn->ignore = orig_insn->ignore_alts; insn->func = orig_insn->func; - if (insn->type != INSN_JUMP_CONDITIONAL && - insn->type != INSN_JUMP_UNCONDITIONAL) + if (!is_static_jump(insn)) continue; if (!insn->immediate) From dc4197236c20e761f2007c641afd193f81a00a74 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 10 Feb 2020 12:32:40 -0600 Subject: [PATCH 0092/2231] objtool: Add relocation check for alternative sections Relocations in alternative code can be dangerous, because the code is copy/pasted to the text section after relocations have been resolved, which can corrupt PC-relative addresses. However, relocations might be acceptable in some cases, depending on the architecture. For example, the x86 alternatives code manually fixes up the target addresses for PC-relative jumps and calls. So disallow relocations in alternative code, except where the x86 arch code allows it. This code may need to be tweaked for other arches when objtool gets support for them. Suggested-by: Linus Torvalds Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Reviewed-by: Julien Thierry Link: https://lkml.kernel.org/r/7b90b68d093311e4e8f6b504a9e1c758fd7e0002.1581359535.git.jpoimboe@redhat.com --- tools/objtool/check.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 9016ae1c0c55..b038de2ccd71 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -768,6 +768,27 @@ static int handle_group_alt(struct objtool_file *file, insn->ignore = orig_insn->ignore_alts; insn->func = orig_insn->func; + /* + * Since alternative replacement code is copy/pasted by the + * kernel after applying relocations, generally such code can't + * have relative-address relocation references to outside the + * .altinstr_replacement section, unless the arch's + * alternatives code can adjust the relative offsets + * accordingly. + * + * The x86 alternatives code adjusts the offsets only when it + * encounters a branch instruction at the very beginning of the + * replacement group. + */ + if ((insn->offset != special_alt->new_off || + (insn->type != INSN_CALL && !is_static_jump(insn))) && + find_rela_by_dest_range(insn->sec, insn->offset, insn->len)) { + + WARN_FUNC("unsupported relocation in alternatives section", + insn->sec, insn->offset); + return -1; + } + if (!is_static_jump(insn)) continue; From 1565e8e8ef0bbccabf62152b93c6505b8041b1e5 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 11 Feb 2020 20:48:24 +0800 Subject: [PATCH 0093/2231] dt-bindings: spi: imx: Add i.MX8MM/i.MX8MN/i.MX8MP compatible Add compatible for imx8mm/imx8mn/imx8mp. Signed-off-by: Anson Huang Acked-by: Rob Herring Link: https://lore.kernel.org/r/1581425307-18567-1-git-send-email-Anson.Huang@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt index 2d3264140cc5..33bc58f4cf4b 100644 --- a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt +++ b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt @@ -10,7 +10,10 @@ Required properties: - "fsl,imx35-cspi" for SPI compatible with the one integrated on i.MX35 - "fsl,imx51-ecspi" for SPI compatible with the one integrated on i.MX51 - "fsl,imx53-ecspi" for SPI compatible with the one integrated on i.MX53 and later Soc - - "fsl,imx8mq-ecspi" for SPI compatible with the one integrated on i.MX8M + - "fsl,imx8mq-ecspi" for SPI compatible with the one integrated on i.MX8MQ + - "fsl,imx8mm-ecspi" for SPI compatible with the one integrated on i.MX8MM + - "fsl,imx8mn-ecspi" for SPI compatible with the one integrated on i.MX8MN + - "fsl,imx8mp-ecspi" for SPI compatible with the one integrated on i.MX8MP - reg : Offset and length of the register set for the device - interrupts : Should contain CSPI/eCSPI interrupt - clocks : Clock specifiers for both ipg and per clocks. From 032b423b31ad8ce70ec5665163231f37bd57d209 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 11 Feb 2020 22:03:34 +0800 Subject: [PATCH 0094/2231] staging: wfx: remove set but not used variable 'tx_priv' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/staging/wfx/queue.c: In function wfx_tx_queues_get: drivers/staging/wfx/queue.c:484:28: warning: variable tx_priv set but not used [-Wunused-but-set-variable] commit 2e57865e79cf ("staging: wfx: pspoll_mask make no sense") left behind this unused variable. Reported-by: Hulk Robot Signed-off-by: YueHaibing Reviewed-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200211140334.55248-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/queue.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/staging/wfx/queue.c b/drivers/staging/wfx/queue.c index 0bcc61feee1d..c73d158668e3 100644 --- a/drivers/staging/wfx/queue.c +++ b/drivers/staging/wfx/queue.c @@ -481,7 +481,6 @@ struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev) struct wfx_queue *vif_queue = NULL; u32 tx_allowed_mask = 0; u32 vif_tx_allowed_mask = 0; - const struct wfx_tx_priv *tx_priv = NULL; struct wfx_vif *wvif; int not_found; int burst; @@ -541,7 +540,6 @@ struct hif_msg *wfx_tx_queues_get(struct wfx_dev *wdev) skb = wfx_tx_queue_get(wdev, queue, tx_allowed_mask); if (!skb) continue; - tx_priv = wfx_skb_tx_priv(skb); hif = (struct hif_msg *) skb->data; wvif = wdev_to_wvif(wdev, hif->interface); WARN_ON(!wvif); From 4033714d6cbe04893aa0708d1fcaa45dd8eb3f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 11 Feb 2020 11:35:01 +0100 Subject: [PATCH 0095/2231] staging: wfx: fix init/remove vs IRQ race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current code races in init/exit with interrupt handlers. This is noticed by the warning below. Fix it by using devres for ordering allocations and IRQ de/registration. WARNING: CPU: 0 PID: 827 at drivers/staging/wfx/bus_spi.c:142 wfx_spi_irq_handler+0x5c/0x64 [wfx] race condition in driver init/deinit Cc: stable@vger.kernel.org Fixes: 0096214a59a7 ("staging: wfx: add support for I/O access") Signed-off-by: Michał Mirosław Reviewed-by: Jérôme Pouiller Link: https://lore.kernel.org/r/f0c66cbb3110c2736cd4357c753fba8c14ee3aee.1581416843.git.mirq-linux@rere.qmqm.pl Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/bus_sdio.c | 15 ++++++--------- drivers/staging/wfx/bus_spi.c | 27 ++++++++++++++------------- drivers/staging/wfx/main.c | 21 +++++++++++++-------- drivers/staging/wfx/main.h | 1 - 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c index f8901164c206..5450bd5e1b5d 100644 --- a/drivers/staging/wfx/bus_sdio.c +++ b/drivers/staging/wfx/bus_sdio.c @@ -200,25 +200,23 @@ static int wfx_sdio_probe(struct sdio_func *func, if (ret) goto err0; - ret = wfx_sdio_irq_subscribe(bus); - if (ret) - goto err1; - bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata, &wfx_sdio_hwbus_ops, bus); if (!bus->core) { ret = -EIO; - goto err2; + goto err1; } + ret = wfx_sdio_irq_subscribe(bus); + if (ret) + goto err1; + ret = wfx_probe(bus->core); if (ret) - goto err3; + goto err2; return 0; -err3: - wfx_free_common(bus->core); err2: wfx_sdio_irq_unsubscribe(bus); err1: @@ -234,7 +232,6 @@ static void wfx_sdio_remove(struct sdio_func *func) struct wfx_sdio_priv *bus = sdio_get_drvdata(func); wfx_release(bus->core); - wfx_free_common(bus->core); wfx_sdio_irq_unsubscribe(bus); sdio_claim_host(func); sdio_disable_func(func); diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index 40bc33035de2..605ad74068b7 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -154,6 +154,11 @@ static void wfx_spi_request_rx(struct work_struct *work) wfx_bh_request_rx(bus->core); } +static void wfx_flush_irq_work(void *w) +{ + flush_work(w); +} + static size_t wfx_spi_align_size(void *priv, size_t size) { // Most of SPI controllers avoid DMA if buffer size is not 32bit aligned @@ -207,22 +212,23 @@ static int wfx_spi_probe(struct spi_device *func) udelay(2000); } - ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler, - IRQF_TRIGGER_RISING, "wfx", bus); - if (ret) - return ret; - INIT_WORK(&bus->request_rx, wfx_spi_request_rx); bus->core = wfx_init_common(&func->dev, &wfx_spi_pdata, &wfx_spi_hwbus_ops, bus); if (!bus->core) return -EIO; - ret = wfx_probe(bus->core); + ret = devm_add_action_or_reset(&func->dev, wfx_flush_irq_work, + &bus->request_rx); if (ret) - wfx_free_common(bus->core); + return ret; - return ret; + ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler, + IRQF_TRIGGER_RISING, "wfx", bus); + if (ret) + return ret; + + return wfx_probe(bus->core); } static int wfx_spi_remove(struct spi_device *func) @@ -230,11 +236,6 @@ static int wfx_spi_remove(struct spi_device *func) struct wfx_spi_priv *bus = spi_get_drvdata(func); wfx_release(bus->core); - wfx_free_common(bus->core); - // A few IRQ will be sent during device release. Hopefully, no IRQ - // should happen after wdev/wvif are released. - devm_free_irq(&func->dev, func->irq, bus); - flush_work(&bus->request_rx); return 0; } diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 84adad64fc30..76b2ff7fc7fe 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -262,6 +262,16 @@ static int wfx_send_pdata_pds(struct wfx_dev *wdev) return ret; } +static void wfx_free_common(void *data) +{ + struct wfx_dev *wdev = data; + + mutex_destroy(&wdev->rx_stats_lock); + mutex_destroy(&wdev->conf_mutex); + wfx_tx_queues_deinit(wdev); + ieee80211_free_hw(wdev->hw); +} + struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_data *pdata, const struct hwbus_ops *hwbus_ops, @@ -332,15 +342,10 @@ struct wfx_dev *wfx_init_common(struct device *dev, wfx_init_hif_cmd(&wdev->hif_cmd); wfx_tx_queues_init(wdev); - return wdev; -} + if (devm_add_action_or_reset(dev, wfx_free_common, wdev)) + return NULL; -void wfx_free_common(struct wfx_dev *wdev) -{ - mutex_destroy(&wdev->rx_stats_lock); - mutex_destroy(&wdev->conf_mutex); - wfx_tx_queues_deinit(wdev); - ieee80211_free_hw(wdev->hw); + return wdev; } int wfx_probe(struct wfx_dev *wdev) diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h index 875f8c227803..9c9410072def 100644 --- a/drivers/staging/wfx/main.h +++ b/drivers/staging/wfx/main.h @@ -34,7 +34,6 @@ struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_data *pdata, const struct hwbus_ops *hwbus_ops, void *hwbus_priv); -void wfx_free_common(struct wfx_dev *wdev); int wfx_probe(struct wfx_dev *wdev); void wfx_release(struct wfx_dev *wdev); From e2525a95cc0887c7dc0549cb5d0ac3e796e1d54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 11 Feb 2020 11:35:01 +0100 Subject: [PATCH 0096/2231] staging: wfx: annotate nested gc_list vs tx queue locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lockdep is complaining about recursive locking, because it can't make a difference between locked skb_queues. Annotate nested locks and avoid double bh_disable/enable. [...] insmod/815 is trying to acquire lock: cb7d6418 (&(&list->lock)->rlock){+...}, at: wfx_tx_queues_clear+0xfc/0x198 [wfx] but task is already holding lock: cb7d61f4 (&(&list->lock)->rlock){+...}, at: wfx_tx_queues_clear+0xa0/0x198 [wfx] [...] Possible unsafe locking scenario: CPU0 ---- lock(&(&list->lock)->rlock); lock(&(&list->lock)->rlock); Cc: stable@vger.kernel.org Fixes: 9bca45f3d692 ("staging: wfx: allow to send 802.11 frames") Signed-off-by: Michał Mirosław Link: https://lore.kernel.org/r/5e30397af95854b4a7deea073b730c00229f42ba.1581416843.git.mirq-linux@rere.qmqm.pl Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/queue.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/wfx/queue.c b/drivers/staging/wfx/queue.c index c73d158668e3..ed6dc8297738 100644 --- a/drivers/staging/wfx/queue.c +++ b/drivers/staging/wfx/queue.c @@ -130,12 +130,12 @@ static void wfx_tx_queue_clear(struct wfx_dev *wdev, struct wfx_queue *queue, spin_lock_bh(&queue->queue.lock); while ((item = __skb_dequeue(&queue->queue)) != NULL) skb_queue_head(gc_list, item); - spin_lock_bh(&stats->pending.lock); + spin_lock_nested(&stats->pending.lock, 1); for (i = 0; i < ARRAY_SIZE(stats->link_map_cache); ++i) { stats->link_map_cache[i] -= queue->link_map_cache[i]; queue->link_map_cache[i] = 0; } - spin_unlock_bh(&stats->pending.lock); + spin_unlock(&stats->pending.lock); spin_unlock_bh(&queue->queue.lock); } @@ -207,9 +207,9 @@ void wfx_tx_queue_put(struct wfx_dev *wdev, struct wfx_queue *queue, ++queue->link_map_cache[tx_priv->link_id]; - spin_lock_bh(&stats->pending.lock); + spin_lock_nested(&stats->pending.lock, 1); ++stats->link_map_cache[tx_priv->link_id]; - spin_unlock_bh(&stats->pending.lock); + spin_unlock(&stats->pending.lock); spin_unlock_bh(&queue->queue.lock); } @@ -237,11 +237,11 @@ static struct sk_buff *wfx_tx_queue_get(struct wfx_dev *wdev, __skb_unlink(skb, &queue->queue); --queue->link_map_cache[tx_priv->link_id]; - spin_lock_bh(&stats->pending.lock); + spin_lock_nested(&stats->pending.lock, 1); __skb_queue_tail(&stats->pending, skb); if (!--stats->link_map_cache[tx_priv->link_id]) wakeup_stats = true; - spin_unlock_bh(&stats->pending.lock); + spin_unlock(&stats->pending.lock); } spin_unlock_bh(&queue->queue.lock); if (wakeup_stats) @@ -259,10 +259,10 @@ int wfx_pending_requeue(struct wfx_dev *wdev, struct sk_buff *skb) spin_lock_bh(&queue->queue.lock); ++queue->link_map_cache[tx_priv->link_id]; - spin_lock_bh(&stats->pending.lock); + spin_lock_nested(&stats->pending.lock, 1); ++stats->link_map_cache[tx_priv->link_id]; __skb_unlink(skb, &stats->pending); - spin_unlock_bh(&stats->pending.lock); + spin_unlock(&stats->pending.lock); __skb_queue_tail(&queue->queue, skb); spin_unlock_bh(&queue->queue.lock); return 0; From eec6e3ee636ec3adaa85ebe4b4acaacfcf06277e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 11 Feb 2020 11:35:02 +0100 Subject: [PATCH 0097/2231] staging: wfx: add proper "compatible" string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add "compatible" string matching "vendor,chip" template and proper GPIO flags handling. Keep support for old name and reset polarity for older devicetrees. Cc: stable@vger.kernel.org # d3a5bcb4a17f ("gpio: add gpiod_toggle_active_low()") Cc: stable@vger.kernel.org Fixes: 0096214a59a7 ("staging: wfx: add support for I/O access") Signed-off-by: Michał Mirosław Link: https://lore.kernel.org/r/0e6dda06f145676861860f073a53dc95987c7ab5.1581416843.git.mirq-linux@rere.qmqm.pl Signed-off-by: Greg Kroah-Hartman --- .../bindings/net/wireless/siliabs,wfx.txt | 7 ++++--- drivers/staging/wfx/bus_spi.c | 14 ++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt b/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt index 26de6762b942..52f97673da1e 100644 --- a/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt +++ b/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt @@ -6,7 +6,7 @@ SPI You have to declare the WFxxx chip in your device tree. Required properties: - - compatible: Should be "silabs,wfx-spi" + - compatible: Should be "silabs,wf200" - reg: Chip select address of device - spi-max-frequency: Maximum SPI clocking speed of device in Hz - interrupts-extended: Should contain interrupt line (interrupt-parent + @@ -15,6 +15,7 @@ Required properties: Optional properties: - reset-gpios: phandle of gpio that will be used to reset chip during probe. Without this property, you may encounter issues with warm boot. + (Legacy: when compatible == "silabs,wfx-spi", the gpio is inverted.) Please consult Documentation/devicetree/bindings/spi/spi-bus.txt for optional SPI connection related properties, @@ -23,12 +24,12 @@ Example: &spi1 { wfx { - compatible = "silabs,wfx-spi"; + compatible = "silabs,wf200"; pinctrl-names = "default"; pinctrl-0 = <&wfx_irq &wfx_gpios>; interrupts-extended = <&gpio 16 IRQ_TYPE_EDGE_RISING>; wakeup-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 13 GPIO_ACTIVE_LOW>; reg = <0>; spi-max-frequency = <42000000>; }; diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index 605ad74068b7..d6a75bd61595 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -27,6 +27,8 @@ MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none."); #define SET_WRITE 0x7FFF /* usage: and operation */ #define SET_READ 0x8000 /* usage: or operation */ +#define WFX_RESET_INVERTED 1 + static const struct wfx_platform_data wfx_spi_pdata = { .file_fw = "wfm_wf200", .file_pds = "wf200.pds", @@ -206,9 +208,11 @@ static int wfx_spi_probe(struct spi_device *func) if (!bus->gpio_reset) { dev_warn(&func->dev, "try to load firmware anyway\n"); } else { - gpiod_set_value(bus->gpio_reset, 0); - udelay(100); + if (spi_get_device_id(func)->driver_data & WFX_RESET_INVERTED) + gpiod_toggle_active_low(bus->gpio_reset); gpiod_set_value(bus->gpio_reset, 1); + udelay(100); + gpiod_set_value(bus->gpio_reset, 0); udelay(2000); } @@ -245,14 +249,16 @@ static int wfx_spi_remove(struct spi_device *func) * stripped. */ static const struct spi_device_id wfx_spi_id[] = { - { "wfx-spi", 0 }, + { "wfx-spi", WFX_RESET_INVERTED }, + { "wf200", 0 }, { }, }; MODULE_DEVICE_TABLE(spi, wfx_spi_id); #ifdef CONFIG_OF static const struct of_device_id wfx_spi_of_match[] = { - { .compatible = "silabs,wfx-spi" }, + { .compatible = "silabs,wfx-spi", .data = (void *)WFX_RESET_INVERTED }, + { .compatible = "silabs,wf200" }, { }, }; MODULE_DEVICE_TABLE(of, wfx_spi_of_match); From 7f70c919ffb8cd22c73bbfb5db3ca56d54d90408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 11 Feb 2020 11:35:02 +0100 Subject: [PATCH 0098/2231] staging: wfx: follow compatible = vendor,chip format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As for SPI, follow "vendor,chip" format 'compatible' string also for SDIO bus. Fixes: 0096214a59a7 ("staging: wfx: add support for I/O access") Signed-off-by: Michał Mirosław Link: https://lore.kernel.org/r/96b95d52d0b613065fe655f1d0fe9d7c6adf65fb.1581416843.git.mirq-linux@rere.qmqm.pl Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/net/wireless/siliabs,wfx.txt | 4 ++-- drivers/staging/wfx/bus_sdio.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt b/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt index 52f97673da1e..ffec79c14786 100644 --- a/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt +++ b/drivers/staging/wfx/Documentation/devicetree/bindings/net/wireless/siliabs,wfx.txt @@ -45,7 +45,7 @@ case. Thus declaring WFxxx chip in device tree is strongly recommended (and may become mandatory in the future). Required properties: - - compatible: Should be "silabs,wfx-sdio" + - compatible: Should be "silabs,wf200" - reg: Should be 1 In addition, it is recommended to declare a mmc-pwrseq on SDIO host above WFx. @@ -71,7 +71,7 @@ Example: #size = <0>; mmc@1 { - compatible = "silabs,wfx-sdio"; + compatible = "silabs,wf200"; reg = <1>; pinctrl-names = "default"; pinctrl-0 = <&wfx_wakeup>; diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c index 5450bd5e1b5d..dedc3ff58d3e 100644 --- a/drivers/staging/wfx/bus_sdio.c +++ b/drivers/staging/wfx/bus_sdio.c @@ -251,6 +251,7 @@ MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids); #ifdef CONFIG_OF static const struct of_device_id wfx_sdio_of_match[] = { { .compatible = "silabs,wfx-sdio" }, + { .compatible = "silabs,wf200" }, { }, }; MODULE_DEVICE_TABLE(of, wfx_sdio_of_match); From 40a97d55a6b7f52df0a4ff59b860a4d4c2a0283a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 11 Feb 2020 11:35:03 +0100 Subject: [PATCH 0099/2231] staging: wfx: use sleeping gpio accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver calls GPIO get/set only from non-atomic context and so can use any GPIOs. Signed-off-by: Michał Mirosław Link: https://lore.kernel.org/r/cb19e7c521712d5a166e0b7e9cac4450798fdce0.1581416843.git.mirq-linux@rere.qmqm.pl Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/bh.c | 6 +++--- drivers/staging/wfx/bus_spi.c | 4 ++-- drivers/staging/wfx/main.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index 983c41d1fe7c..c6319ab7e71a 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -20,10 +20,10 @@ static void device_wakeup(struct wfx_dev *wdev) { if (!wdev->pdata.gpio_wakeup) return; - if (gpiod_get_value(wdev->pdata.gpio_wakeup)) + if (gpiod_get_value_cansleep(wdev->pdata.gpio_wakeup)) return; - gpiod_set_value(wdev->pdata.gpio_wakeup, 1); + gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 1); if (wfx_api_older_than(wdev, 1, 4)) { if (!completion_done(&wdev->hif.ctrl_ready)) udelay(2000); @@ -45,7 +45,7 @@ static void device_release(struct wfx_dev *wdev) if (!wdev->pdata.gpio_wakeup) return; - gpiod_set_value(wdev->pdata.gpio_wakeup, 0); + gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 0); } static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf) diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index d6a75bd61595..e3cd12592662 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -210,9 +210,9 @@ static int wfx_spi_probe(struct spi_device *func) } else { if (spi_get_device_id(func)->driver_data & WFX_RESET_INVERTED) gpiod_toggle_active_low(bus->gpio_reset); - gpiod_set_value(bus->gpio_reset, 1); + gpiod_set_value_cansleep(bus->gpio_reset, 1); udelay(100); - gpiod_set_value(bus->gpio_reset, 0); + gpiod_set_value_cansleep(bus->gpio_reset, 0); udelay(2000); } diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c index 76b2ff7fc7fe..3c4c240229ad 100644 --- a/drivers/staging/wfx/main.c +++ b/drivers/staging/wfx/main.c @@ -425,7 +425,7 @@ int wfx_probe(struct wfx_dev *wdev) "enable 'quiescent' power mode with gpio %d and PDS file %s\n", desc_to_gpio(wdev->pdata.gpio_wakeup), wdev->pdata.file_pds); - gpiod_set_value(wdev->pdata.gpio_wakeup, 1); + gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 1); control_reg_write(wdev, 0); hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_QUIESCENT); } else { From d2bdc492916046da4abe07ec2373bd962f2224e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Tue, 11 Feb 2020 11:35:04 +0100 Subject: [PATCH 0100/2231] staging: wfx: use more power-efficient sleep for reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace udelay() with usleep_range() as all uses are in a sleepable context. Signed-off-by: Michał Mirosław Link: https://lore.kernel.org/r/59e1e4e5bd80c1879ef36eaa59916e47005dbb04.1581416843.git.mirq-linux@rere.qmqm.pl Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/bh.c | 2 +- drivers/staging/wfx/bus_spi.c | 4 ++-- drivers/staging/wfx/hwio.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index c6319ab7e71a..9fcab00a3733 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -26,7 +26,7 @@ static void device_wakeup(struct wfx_dev *wdev) gpiod_set_value_cansleep(wdev->pdata.gpio_wakeup, 1); if (wfx_api_older_than(wdev, 1, 4)) { if (!completion_done(&wdev->hif.ctrl_ready)) - udelay(2000); + usleep_range(2000, 2500); } else { // completion.h does not provide any function to wait // completion without consume it (a kind of diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c index e3cd12592662..61e99b09decb 100644 --- a/drivers/staging/wfx/bus_spi.c +++ b/drivers/staging/wfx/bus_spi.c @@ -211,9 +211,9 @@ static int wfx_spi_probe(struct spi_device *func) if (spi_get_device_id(func)->driver_data & WFX_RESET_INVERTED) gpiod_toggle_active_low(bus->gpio_reset); gpiod_set_value_cansleep(bus->gpio_reset, 1); - udelay(100); + usleep_range(100, 150); gpiod_set_value_cansleep(bus->gpio_reset, 0); - udelay(2000); + usleep_range(2000, 2500); } INIT_WORK(&bus->request_rx, wfx_spi_request_rx); diff --git a/drivers/staging/wfx/hwio.c b/drivers/staging/wfx/hwio.c index 47e04c59ed93..d3a141d95a0e 100644 --- a/drivers/staging/wfx/hwio.c +++ b/drivers/staging/wfx/hwio.c @@ -142,7 +142,7 @@ static int indirect_read(struct wfx_dev *wdev, int reg, u32 addr, void *buf, goto err; if (!(cfg & prefetch)) break; - udelay(200); + usleep_range(200, 250); } if (i == 20) { ret = -ETIMEDOUT; From a474df5c1484097620362ff26877a244a27b944b Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Tue, 11 Feb 2020 09:57:05 +0000 Subject: [PATCH 0101/2231] staging: wilc1000: remove use of vendor specific IE for p2p handling Remove the use of vendor specific IE parameter to decide p2p_GO/p2p_Client roles between two 'wilc' device. Previously p2p group formation between two 'wilc' device make use of vendor IE for roles decision. The role is decided based on the 'go_intent' value. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200211152802.6096-1-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/cfg80211.c | 72 ++--------------------------- drivers/staging/wilc1000/netdev.c | 2 - drivers/staging/wilc1000/netdev.h | 7 --- 3 files changed, 4 insertions(+), 77 deletions(-) diff --git a/drivers/staging/wilc1000/cfg80211.c b/drivers/staging/wilc1000/cfg80211.c index 4863e516ff13..7afbc475b3ea 100644 --- a/drivers/staging/wilc1000/cfg80211.c +++ b/drivers/staging/wilc1000/cfg80211.c @@ -68,7 +68,6 @@ struct wilc_p2p_mgmt_data { }; static const u8 p2p_oui[] = {0x50, 0x6f, 0x9A, 0x09}; -static const u8 p2p_vendor_spec[] = {0xdd, 0x05, 0x00, 0x08, 0x40, 0x03}; static void cfg_scan_result(enum scan_event scan_event, struct wilc_rcvd_net_info *info, void *user_void) @@ -172,9 +171,6 @@ static void cfg_connect_result(enum conn_event conn_disconn_evt, u8 mac_status, } else if (conn_disconn_evt == CONN_DISCONN_EVENT_DISCONN_NOTIF) { u16 reason = 0; - priv->p2p.local_random = 0x01; - priv->p2p.recv_random = 0x00; - priv->p2p.is_wilc_ie = false; eth_zero_addr(priv->associated_bss); wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE); @@ -446,9 +442,6 @@ static int disconnect(struct wiphy *wiphy, struct net_device *dev, wilc->sta_ch = WILC_INVALID_CHANNEL; wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE); - priv->p2p.local_random = 0x01; - priv->p2p.recv_random = 0x00; - priv->p2p.is_wilc_ie = false; priv->hif_drv->p2p_timeout = 0; ret = wilc_disconnect(vif); @@ -934,9 +927,6 @@ static void wilc_wfi_cfg_parse_rx_action(u8 *buf, u32 len, u8 sta_ch) u8 channel_list_attr_index = 0; while (index < len) { - if (buf[index] == GO_INTENT_ATTR_ID) - buf[index + 3] = (buf[index + 3] & 0x01) | (0x00 << 1); - if (buf[index] == CHANLIST_ATTR_ID) channel_list_attr_index = index; else if (buf[index] == OPERCHAN_ATTR_ID) @@ -956,12 +946,6 @@ static void wilc_wfi_cfg_parse_tx_action(u8 *buf, u32 len, bool oper_ch, u8 channel_list_attr_index = 0; while (index < len) { - if (buf[index] == GO_INTENT_ATTR_ID) { - buf[index + 3] = (buf[index + 3] & 0x01) | (0x0f << 1); - - break; - } - if (buf[index] == CHANLIST_ATTR_ID) channel_list_attr_index = index; else if (buf[index] == OPERCHAN_ATTR_ID) @@ -981,24 +965,6 @@ static void wilc_wfi_cfg_parse_rx_vendor_spec(struct wilc_priv *priv, u8 *buff, struct wilc_vif *vif = netdev_priv(priv->dev); subtype = buff[P2P_PUB_ACTION_SUBTYPE]; - if ((subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) && - !priv->p2p.is_wilc_ie) { - for (i = P2P_PUB_ACTION_SUBTYPE; i < size; i++) { - if (!memcmp(p2p_vendor_spec, &buff[i], 6)) { - priv->p2p.recv_random = buff[i + 6]; - priv->p2p.is_wilc_ie = true; - break; - } - } - } - - if (priv->p2p.local_random <= priv->p2p.recv_random) { - netdev_dbg(vif->ndev, - "PEER WILL BE GO LocaRand=%02x RecvRand %02x\n", - priv->p2p.local_random, priv->p2p.recv_random); - return; - } - if (subtype == GO_NEG_REQ || subtype == GO_NEG_RSP || subtype == P2P_INV_REQ || subtype == P2P_INV_RSP) { for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < size; i++) { @@ -1051,8 +1017,6 @@ void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size) return; } if (buff[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) { - u8 subtype = buff[P2P_PUB_ACTION_SUBTYPE]; - switch (buff[ACTION_SUBTYPE_ID]) { case GAS_INITIAL_REQ: case GAS_INITIAL_RSP: @@ -1063,10 +1027,6 @@ void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size) wilc_wfi_cfg_parse_rx_vendor_spec(priv, buff, size); - if ((subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) && - priv->p2p.is_wilc_ie) - size -= 7; - break; default: @@ -1167,17 +1127,8 @@ static void wilc_wfi_cfg_tx_vendor_spec(struct wilc_priv *priv, u8 subtype = buf[P2P_PUB_ACTION_SUBTYPE]; struct wilc_vif *vif = netdev_priv(priv->dev); - if (subtype == GO_NEG_REQ || subtype == GO_NEG_RSP) { - if (priv->p2p.local_random == 1 && - priv->p2p.recv_random < priv->p2p.local_random) { - get_random_bytes(&priv->p2p.local_random, 1); - priv->p2p.local_random++; - } - } - - if (priv->p2p.local_random <= priv->p2p.recv_random || - !(subtype == GO_NEG_REQ || subtype == GO_NEG_RSP || - subtype == P2P_INV_REQ || subtype == P2P_INV_RSP)) + if (subtype != GO_NEG_REQ && subtype != GO_NEG_RSP && + subtype != P2P_INV_REQ && subtype != P2P_INV_RSP) return; for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < len; i++) { @@ -1196,15 +1147,6 @@ static void wilc_wfi_cfg_tx_vendor_spec(struct wilc_priv *priv, break; } } - - if (subtype != P2P_INV_REQ && subtype != P2P_INV_RSP) { - int vendor_spec_len = sizeof(p2p_vendor_spec); - - memcpy(&mgmt_tx->buff[len], p2p_vendor_spec, - vendor_spec_len); - mgmt_tx->buff[len + vendor_spec_len] = priv->p2p.local_random; - mgmt_tx->size = buf_len; - } } static int mgmt_tx(struct wiphy *wiphy, @@ -1221,8 +1163,6 @@ static int mgmt_tx(struct wiphy *wiphy, struct wilc_vif *vif = netdev_priv(wdev->netdev); struct wilc_priv *priv = &vif->priv; struct host_if_drv *wfi_drv = priv->hif_drv; - u32 buf_len = len + sizeof(p2p_vendor_spec) + - sizeof(priv->p2p.local_random); int ret = 0; *cookie = prandom_u32(); @@ -1238,7 +1178,7 @@ static int mgmt_tx(struct wiphy *wiphy, goto out; } - mgmt_tx->buff = kmalloc(buf_len, GFP_KERNEL); + mgmt_tx->buff = kmalloc(len, GFP_KERNEL); if (!mgmt_tx->buff) { ret = -ENOMEM; kfree(mgmt_tx); @@ -1272,7 +1212,7 @@ static int mgmt_tx(struct wiphy *wiphy, if (!memcmp(p2p_oui, &buf[ACTION_SUBTYPE_ID + 1], 4)) wilc_wfi_cfg_tx_vendor_spec(priv, mgmt_tx, params, vif->iftype, - buf_len); + len); else netdev_dbg(vif->ndev, "Not a P2P public action frame\n"); @@ -1400,10 +1340,6 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, struct wilc_vif *vif = netdev_priv(dev); struct wilc_priv *priv = &vif->priv; - priv->p2p.local_random = 0x01; - priv->p2p.recv_random = 0x00; - priv->p2p.is_wilc_ie = false; - switch (type) { case NL80211_IFTYPE_STATION: vif->connecting = false; diff --git a/drivers/staging/wilc1000/netdev.c b/drivers/staging/wilc1000/netdev.c index fce5bf2d82fa..045f5cdfdca0 100644 --- a/drivers/staging/wilc1000/netdev.c +++ b/drivers/staging/wilc1000/netdev.c @@ -577,7 +577,6 @@ static int wilc_mac_open(struct net_device *ndev) { struct wilc_vif *vif = netdev_priv(ndev); struct wilc *wl = vif->wilc; - struct wilc_priv *priv = wdev_priv(vif->ndev->ieee80211_ptr); unsigned char mac_add[ETH_ALEN] = {0}; int ret = 0; @@ -621,7 +620,6 @@ static int wilc_mac_open(struct net_device *ndev) vif->frame_reg[1].reg); netif_wake_queue(ndev); wl->open_ifcs++; - priv->p2p.local_random = 0x01; vif->mac_opened = 1; return 0; } diff --git a/drivers/staging/wilc1000/netdev.h b/drivers/staging/wilc1000/netdev.h index d5f7a6037fbc..c475e78e3a69 100644 --- a/drivers/staging/wilc1000/netdev.h +++ b/drivers/staging/wilc1000/netdev.h @@ -66,12 +66,6 @@ struct wilc_wfi_p2p_listen_params { u64 listen_cookie; }; -struct wilc_p2p_var { - u8 local_random; - u8 recv_random; - bool is_wilc_ie; -}; - static const u32 wilc_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, @@ -155,7 +149,6 @@ struct wilc_priv { struct mutex scan_req_lock; bool p2p_listen_state; int scanned_cnt; - struct wilc_p2p_var p2p; u64 inc_roc_cookie; }; From 0f4f67b551426cf912129e104cd44d99216d29e9 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Tue, 11 Feb 2020 09:57:09 +0000 Subject: [PATCH 0102/2231] staging: wilc1000: directly fetch 'priv' handler from 'vif' instance The 'priv' handler is already present in 'vif' struct so directly fetch its value from vif handler in wilc_handle_roc_expired(). Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200211152802.6096-2-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/hif.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/wilc1000/hif.c b/drivers/staging/wilc1000/hif.c index 658790bd465b..c8c41c2df4ec 100644 --- a/drivers/staging/wilc1000/hif.c +++ b/drivers/staging/wilc1000/hif.c @@ -861,9 +861,8 @@ static int wilc_handle_roc_expired(struct wilc_vif *vif, u64 cookie) struct wid wid; int result; struct host_if_drv *hif_drv = vif->hif_drv; - struct wilc_priv *priv = wdev_priv(vif->ndev->ieee80211_ptr); - if (priv->p2p_listen_state) { + if (vif->priv.p2p_listen_state) { remain_on_chan_flag = false; wid.id = WID_REMAIN_ON_CHAN; wid.type = WID_STR; From b264fad30bf68862e0bbc45b4629375fa92f4c75 Mon Sep 17 00:00:00 2001 From: Mohana Datta Yelugoti Date: Tue, 11 Feb 2020 15:28:13 +0530 Subject: [PATCH 0103/2231] staging: qlge: qlge_main.c: fix style issues This patch fixes "WARNING: Missing a blank line after declarations" generated from checkpatch.pl by adding a blank line after declarations. Signed-off-by: Mohana Datta Yelugoti Link: https://lore.kernel.org/r/20200211095813.11426-1-ymdatta.work@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/qlge/qlge_main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/staging/qlge/qlge_main.c b/drivers/staging/qlge/qlge_main.c index 86b9b7314a40..c712e1af90de 100644 --- a/drivers/staging/qlge/qlge_main.c +++ b/drivers/staging/qlge/qlge_main.c @@ -143,6 +143,7 @@ static int ql_sem_trylock(struct ql_adapter *qdev, u32 sem_mask) int ql_sem_spinlock(struct ql_adapter *qdev, u32 sem_mask) { unsigned int wait_count = 30; + do { if (!ql_sem_trylock(qdev, sem_mask)) return 0; @@ -1210,6 +1211,7 @@ static void ql_unmap_send(struct ql_adapter *qdev, struct tx_ring_desc *tx_ring_desc, int mapped) { int i; + for (i = 0; i < mapped; i++) { if (i == 0 || (i == 7 && mapped > 7)) { /* @@ -1290,6 +1292,7 @@ static int ql_map_send(struct ql_adapter *qdev, */ for (frag_idx = 0; frag_idx < frag_cnt; frag_idx++, map_idx++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[frag_idx]; + tbd++; if (frag_idx == 6 && frag_cnt > 7) { /* Let's tack on an sglist. @@ -1649,6 +1652,7 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev, (ib_mac_rsp->flags3 & IB_MAC_IOCB_RSP_V4)) { /* Unfragmented ipv4 UDP frame. */ struct iphdr *iph = (struct iphdr *) skb->data; + if (!(iph->frag_off & htons(IP_MF|IP_OFFSET))) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1818,6 +1822,7 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, * eventually be in trouble. */ int size, i = 0; + sbq_desc = qlge_get_curr_buf(&rx_ring->sbq); pci_unmap_single(qdev->pdev, sbq_desc->dma_addr, SMALL_BUF_MAP_SIZE, PCI_DMA_FROMDEVICE); @@ -1936,6 +1941,7 @@ static void ql_process_mac_split_rx_intr(struct ql_adapter *qdev, (ib_mac_rsp->flags3 & IB_MAC_IOCB_RSP_V4)) { /* Unfragmented ipv4 UDP frame. */ struct iphdr *iph = (struct iphdr *) skb->data; + if (!(iph->frag_off & htons(IP_MF|IP_OFFSET))) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -2391,6 +2397,7 @@ static void qlge_restore_vlan(struct ql_adapter *qdev) static irqreturn_t qlge_msix_rx_isr(int irq, void *dev_id) { struct rx_ring *rx_ring = dev_id; + napi_schedule(&rx_ring->napi); return IRQ_HANDLED; } @@ -2497,6 +2504,7 @@ static int ql_tso(struct sk_buff *skb, struct ob_mac_tso_iocb_req *mac_iocb_ptr) mac_iocb_ptr->flags2 |= OB_MAC_TSO_IOCB_LSO; if (likely(l3_proto == htons(ETH_P_IP))) { struct iphdr *iph = ip_hdr(skb); + iph->check = 0; mac_iocb_ptr->flags1 |= OB_MAC_TSO_IOCB_IP4; tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, @@ -2521,6 +2529,7 @@ static void ql_hw_csum_setup(struct sk_buff *skb, int len; struct iphdr *iph = ip_hdr(skb); __sum16 *check; + mac_iocb_ptr->opcode = OPCODE_OB_MAC_TSO_IOCB; mac_iocb_ptr->frame_len = cpu_to_le32((u32) skb->len); mac_iocb_ptr->net_trans_offset = @@ -4265,6 +4274,7 @@ static int qlge_set_mac_address(struct net_device *ndev, void *p) static void qlge_tx_timeout(struct net_device *ndev, unsigned int txqueue) { struct ql_adapter *qdev = netdev_priv(ndev); + ql_queue_asic_error(qdev); } @@ -4273,6 +4283,7 @@ static void ql_asic_reset_work(struct work_struct *work) struct ql_adapter *qdev = container_of(work, struct ql_adapter, asic_reset_work.work); int status; + rtnl_lock(); status = ql_adapter_down(qdev); if (status) @@ -4344,6 +4355,7 @@ static int ql_get_alt_pcie_func(struct ql_adapter *qdev) static int ql_get_board_info(struct ql_adapter *qdev) { int status; + qdev->func = (ql_read32(qdev, STS) & STS_FUNC_ID_MASK) >> STS_FUNC_ID_SHIFT; if (qdev->func > 3) @@ -4652,6 +4664,7 @@ static void qlge_remove(struct pci_dev *pdev) { struct net_device *ndev = pci_get_drvdata(pdev); struct ql_adapter *qdev = netdev_priv(ndev); + del_timer_sync(&qdev->timer); ql_cancel_all_work_sync(qdev); unregister_netdev(ndev); From 98e5df86e564ca4a93e409fd4fd1bfc59c39ad9f Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:40 +0530 Subject: [PATCH 0104/2231] staging: exfat: Rename variable 'Year' to 'year' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "Year" to "year" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-2-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index e340b36ac519..f6debe0405b9 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -222,7 +222,7 @@ static inline u16 get_row_index(u16 i) #endif struct date_time_t { - u16 Year; + u16 year; u16 Month; u16 Day; u16 Hour; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 7abe76b9237b..90c7a6b504c7 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -59,7 +59,7 @@ static void exfat_write_super(struct super_block *sb); /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ static void exfat_time_fat2unix(struct timespec64 *ts, struct date_time_t *tp) { - ts->tv_sec = mktime64(tp->Year + 1980, tp->Month + 1, tp->Day, + ts->tv_sec = mktime64(tp->year + 1980, tp->Month + 1, tp->Day, tp->Hour, tp->Minute, tp->Second); ts->tv_nsec = tp->MilliSecond * NSEC_PER_MSEC; @@ -80,7 +80,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Hour = 0; tp->Day = 1; tp->Month = 1; - tp->Year = 0; + tp->year = 0; return; } @@ -91,7 +91,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Hour = 23; tp->Day = 31; tp->Month = 12; - tp->Year = 127; + tp->year = 127; return; } @@ -101,7 +101,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Hour = tm.tm_hour; tp->Day = tm.tm_mday; tp->Month = tm.tm_mon + 1; - tp->Year = tm.tm_year + 1900 - 1980; + tp->year = tm.tm_year + 1900 - 1980; } struct timestamp_t *tm_current(struct timestamp_t *tp) @@ -1497,7 +1497,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->Attr = exfat_get_entry_attr(ep); exfat_get_entry_time(ep, &tm, TM_CREATE); - info->CreateTimestamp.Year = tm.year; + info->CreateTimestamp.year = tm.year; info->CreateTimestamp.Month = tm.mon; info->CreateTimestamp.Day = tm.day; info->CreateTimestamp.Hour = tm.hour; @@ -1506,7 +1506,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->CreateTimestamp.MilliSecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); - info->ModifyTimestamp.Year = tm.year; + info->ModifyTimestamp.year = tm.year; info->ModifyTimestamp.Month = tm.mon; info->ModifyTimestamp.Day = tm.day; info->ModifyTimestamp.Hour = tm.hour; @@ -1602,7 +1602,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.hour = info->CreateTimestamp.Hour; tm.day = info->CreateTimestamp.Day; tm.mon = info->CreateTimestamp.Month; - tm.year = info->CreateTimestamp.Year; + tm.year = info->CreateTimestamp.year; exfat_set_entry_time(ep, &tm, TM_CREATE); tm.sec = info->ModifyTimestamp.Second; @@ -1610,7 +1610,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.hour = info->ModifyTimestamp.Hour; tm.day = info->ModifyTimestamp.Day; tm.mon = info->ModifyTimestamp.Month; - tm.year = info->ModifyTimestamp.Year; + tm.year = info->ModifyTimestamp.year; exfat_set_entry_time(ep, &tm, TM_MODIFY); exfat_set_entry_size(ep2, info->Size); @@ -1914,7 +1914,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->Attr = exfat_get_entry_attr(ep); exfat_get_entry_time(ep, &tm, TM_CREATE); - dir_entry->CreateTimestamp.Year = tm.year; + dir_entry->CreateTimestamp.year = tm.year; dir_entry->CreateTimestamp.Month = tm.mon; dir_entry->CreateTimestamp.Day = tm.day; dir_entry->CreateTimestamp.Hour = tm.hour; @@ -1923,7 +1923,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->CreateTimestamp.MilliSecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); - dir_entry->ModifyTimestamp.Year = tm.year; + dir_entry->ModifyTimestamp.year = tm.year; dir_entry->ModifyTimestamp.Month = tm.mon; dir_entry->ModifyTimestamp.Day = tm.day; dir_entry->ModifyTimestamp.Hour = tm.hour; From 7904b260f9da37b5d441a3ad5acdb019f2976cfc Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:41 +0530 Subject: [PATCH 0105/2231] staging: exfat: Rename variable 'Month' to 'month' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "Month" to "month" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-3-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index f6debe0405b9..0f66277ba885 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -223,7 +223,7 @@ static inline u16 get_row_index(u16 i) struct date_time_t { u16 year; - u16 Month; + u16 month; u16 Day; u16 Hour; u16 Minute; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 90c7a6b504c7..df8cf6cb7546 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -59,7 +59,7 @@ static void exfat_write_super(struct super_block *sb); /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ static void exfat_time_fat2unix(struct timespec64 *ts, struct date_time_t *tp) { - ts->tv_sec = mktime64(tp->year + 1980, tp->Month + 1, tp->Day, + ts->tv_sec = mktime64(tp->year + 1980, tp->month + 1, tp->Day, tp->Hour, tp->Minute, tp->Second); ts->tv_nsec = tp->MilliSecond * NSEC_PER_MSEC; @@ -79,7 +79,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Minute = 0; tp->Hour = 0; tp->Day = 1; - tp->Month = 1; + tp->month = 1; tp->year = 0; return; } @@ -90,7 +90,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Minute = 59; tp->Hour = 23; tp->Day = 31; - tp->Month = 12; + tp->month = 12; tp->year = 127; return; } @@ -100,7 +100,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Minute = tm.tm_min; tp->Hour = tm.tm_hour; tp->Day = tm.tm_mday; - tp->Month = tm.tm_mon + 1; + tp->month = tm.tm_mon + 1; tp->year = tm.tm_year + 1900 - 1980; } @@ -1498,7 +1498,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) exfat_get_entry_time(ep, &tm, TM_CREATE); info->CreateTimestamp.year = tm.year; - info->CreateTimestamp.Month = tm.mon; + info->CreateTimestamp.month = tm.mon; info->CreateTimestamp.Day = tm.day; info->CreateTimestamp.Hour = tm.hour; info->CreateTimestamp.Minute = tm.min; @@ -1507,7 +1507,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) exfat_get_entry_time(ep, &tm, TM_MODIFY); info->ModifyTimestamp.year = tm.year; - info->ModifyTimestamp.Month = tm.mon; + info->ModifyTimestamp.month = tm.mon; info->ModifyTimestamp.Day = tm.day; info->ModifyTimestamp.Hour = tm.hour; info->ModifyTimestamp.Minute = tm.min; @@ -1601,7 +1601,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.min = info->CreateTimestamp.Minute; tm.hour = info->CreateTimestamp.Hour; tm.day = info->CreateTimestamp.Day; - tm.mon = info->CreateTimestamp.Month; + tm.mon = info->CreateTimestamp.month; tm.year = info->CreateTimestamp.year; exfat_set_entry_time(ep, &tm, TM_CREATE); @@ -1609,7 +1609,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.min = info->ModifyTimestamp.Minute; tm.hour = info->ModifyTimestamp.Hour; tm.day = info->ModifyTimestamp.Day; - tm.mon = info->ModifyTimestamp.Month; + tm.mon = info->ModifyTimestamp.month; tm.year = info->ModifyTimestamp.year; exfat_set_entry_time(ep, &tm, TM_MODIFY); @@ -1915,7 +1915,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) exfat_get_entry_time(ep, &tm, TM_CREATE); dir_entry->CreateTimestamp.year = tm.year; - dir_entry->CreateTimestamp.Month = tm.mon; + dir_entry->CreateTimestamp.month = tm.mon; dir_entry->CreateTimestamp.Day = tm.day; dir_entry->CreateTimestamp.Hour = tm.hour; dir_entry->CreateTimestamp.Minute = tm.min; @@ -1924,7 +1924,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) exfat_get_entry_time(ep, &tm, TM_MODIFY); dir_entry->ModifyTimestamp.year = tm.year; - dir_entry->ModifyTimestamp.Month = tm.mon; + dir_entry->ModifyTimestamp.month = tm.mon; dir_entry->ModifyTimestamp.Day = tm.day; dir_entry->ModifyTimestamp.Hour = tm.hour; dir_entry->ModifyTimestamp.Minute = tm.min; From 88ff49cc06e5853494f78285f1e2a4315d580847 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:42 +0530 Subject: [PATCH 0106/2231] staging: exfat: Rename variable 'Day' to 'day' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "Day" to "day" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-4-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 0f66277ba885..49f552477448 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -224,7 +224,7 @@ static inline u16 get_row_index(u16 i) struct date_time_t { u16 year; u16 month; - u16 Day; + u16 day; u16 Hour; u16 Minute; u16 Second; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index df8cf6cb7546..e87d838ca41c 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -59,7 +59,7 @@ static void exfat_write_super(struct super_block *sb); /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ static void exfat_time_fat2unix(struct timespec64 *ts, struct date_time_t *tp) { - ts->tv_sec = mktime64(tp->year + 1980, tp->month + 1, tp->Day, + ts->tv_sec = mktime64(tp->year + 1980, tp->month + 1, tp->day, tp->Hour, tp->Minute, tp->Second); ts->tv_nsec = tp->MilliSecond * NSEC_PER_MSEC; @@ -78,7 +78,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Second = 0; tp->Minute = 0; tp->Hour = 0; - tp->Day = 1; + tp->day = 1; tp->month = 1; tp->year = 0; return; @@ -89,7 +89,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Second = 59; tp->Minute = 59; tp->Hour = 23; - tp->Day = 31; + tp->day = 31; tp->month = 12; tp->year = 127; return; @@ -99,7 +99,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->Second = tm.tm_sec; tp->Minute = tm.tm_min; tp->Hour = tm.tm_hour; - tp->Day = tm.tm_mday; + tp->day = tm.tm_mday; tp->month = tm.tm_mon + 1; tp->year = tm.tm_year + 1900 - 1980; } @@ -1499,7 +1499,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) exfat_get_entry_time(ep, &tm, TM_CREATE); info->CreateTimestamp.year = tm.year; info->CreateTimestamp.month = tm.mon; - info->CreateTimestamp.Day = tm.day; + info->CreateTimestamp.day = tm.day; info->CreateTimestamp.Hour = tm.hour; info->CreateTimestamp.Minute = tm.min; info->CreateTimestamp.Second = tm.sec; @@ -1508,7 +1508,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) exfat_get_entry_time(ep, &tm, TM_MODIFY); info->ModifyTimestamp.year = tm.year; info->ModifyTimestamp.month = tm.mon; - info->ModifyTimestamp.Day = tm.day; + info->ModifyTimestamp.day = tm.day; info->ModifyTimestamp.Hour = tm.hour; info->ModifyTimestamp.Minute = tm.min; info->ModifyTimestamp.Second = tm.sec; @@ -1600,7 +1600,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.sec = info->CreateTimestamp.Second; tm.min = info->CreateTimestamp.Minute; tm.hour = info->CreateTimestamp.Hour; - tm.day = info->CreateTimestamp.Day; + tm.day = info->CreateTimestamp.day; tm.mon = info->CreateTimestamp.month; tm.year = info->CreateTimestamp.year; exfat_set_entry_time(ep, &tm, TM_CREATE); @@ -1608,7 +1608,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.sec = info->ModifyTimestamp.Second; tm.min = info->ModifyTimestamp.Minute; tm.hour = info->ModifyTimestamp.Hour; - tm.day = info->ModifyTimestamp.Day; + tm.day = info->ModifyTimestamp.day; tm.mon = info->ModifyTimestamp.month; tm.year = info->ModifyTimestamp.year; exfat_set_entry_time(ep, &tm, TM_MODIFY); @@ -1916,7 +1916,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) exfat_get_entry_time(ep, &tm, TM_CREATE); dir_entry->CreateTimestamp.year = tm.year; dir_entry->CreateTimestamp.month = tm.mon; - dir_entry->CreateTimestamp.Day = tm.day; + dir_entry->CreateTimestamp.day = tm.day; dir_entry->CreateTimestamp.Hour = tm.hour; dir_entry->CreateTimestamp.Minute = tm.min; dir_entry->CreateTimestamp.Second = tm.sec; @@ -1925,7 +1925,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) exfat_get_entry_time(ep, &tm, TM_MODIFY); dir_entry->ModifyTimestamp.year = tm.year; dir_entry->ModifyTimestamp.month = tm.mon; - dir_entry->ModifyTimestamp.Day = tm.day; + dir_entry->ModifyTimestamp.day = tm.day; dir_entry->ModifyTimestamp.Hour = tm.hour; dir_entry->ModifyTimestamp.Minute = tm.min; dir_entry->ModifyTimestamp.Second = tm.sec; From 984e8f24dcd525aa2e40241d33cc184a2b819836 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:43 +0530 Subject: [PATCH 0107/2231] staging: exfat: Rename variable 'Hour' to 'hour' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "Hour" to "hour" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-5-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 49f552477448..b085bf51688c 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -225,7 +225,7 @@ struct date_time_t { u16 year; u16 month; u16 day; - u16 Hour; + u16 hour; u16 Minute; u16 Second; u16 MilliSecond; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index e87d838ca41c..d83c485c5fb4 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -60,7 +60,7 @@ static void exfat_write_super(struct super_block *sb); static void exfat_time_fat2unix(struct timespec64 *ts, struct date_time_t *tp) { ts->tv_sec = mktime64(tp->year + 1980, tp->month + 1, tp->day, - tp->Hour, tp->Minute, tp->Second); + tp->hour, tp->Minute, tp->Second); ts->tv_nsec = tp->MilliSecond * NSEC_PER_MSEC; } @@ -77,7 +77,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->MilliSecond = 0; tp->Second = 0; tp->Minute = 0; - tp->Hour = 0; + tp->hour = 0; tp->day = 1; tp->month = 1; tp->year = 0; @@ -88,7 +88,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->MilliSecond = 999; tp->Second = 59; tp->Minute = 59; - tp->Hour = 23; + tp->hour = 23; tp->day = 31; tp->month = 12; tp->year = 127; @@ -98,7 +98,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->MilliSecond = ts->tv_nsec / NSEC_PER_MSEC; tp->Second = tm.tm_sec; tp->Minute = tm.tm_min; - tp->Hour = tm.tm_hour; + tp->hour = tm.tm_hour; tp->day = tm.tm_mday; tp->month = tm.tm_mon + 1; tp->year = tm.tm_year + 1900 - 1980; @@ -1500,7 +1500,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->CreateTimestamp.year = tm.year; info->CreateTimestamp.month = tm.mon; info->CreateTimestamp.day = tm.day; - info->CreateTimestamp.Hour = tm.hour; + info->CreateTimestamp.hour = tm.hour; info->CreateTimestamp.Minute = tm.min; info->CreateTimestamp.Second = tm.sec; info->CreateTimestamp.MilliSecond = 0; @@ -1509,7 +1509,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->ModifyTimestamp.year = tm.year; info->ModifyTimestamp.month = tm.mon; info->ModifyTimestamp.day = tm.day; - info->ModifyTimestamp.Hour = tm.hour; + info->ModifyTimestamp.hour = tm.hour; info->ModifyTimestamp.Minute = tm.min; info->ModifyTimestamp.Second = tm.sec; info->ModifyTimestamp.MilliSecond = 0; @@ -1599,7 +1599,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) /* set FILE_INFO structure using the acquired struct dentry_t */ tm.sec = info->CreateTimestamp.Second; tm.min = info->CreateTimestamp.Minute; - tm.hour = info->CreateTimestamp.Hour; + tm.hour = info->CreateTimestamp.hour; tm.day = info->CreateTimestamp.day; tm.mon = info->CreateTimestamp.month; tm.year = info->CreateTimestamp.year; @@ -1607,7 +1607,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.sec = info->ModifyTimestamp.Second; tm.min = info->ModifyTimestamp.Minute; - tm.hour = info->ModifyTimestamp.Hour; + tm.hour = info->ModifyTimestamp.hour; tm.day = info->ModifyTimestamp.day; tm.mon = info->ModifyTimestamp.month; tm.year = info->ModifyTimestamp.year; @@ -1917,7 +1917,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->CreateTimestamp.year = tm.year; dir_entry->CreateTimestamp.month = tm.mon; dir_entry->CreateTimestamp.day = tm.day; - dir_entry->CreateTimestamp.Hour = tm.hour; + dir_entry->CreateTimestamp.hour = tm.hour; dir_entry->CreateTimestamp.Minute = tm.min; dir_entry->CreateTimestamp.Second = tm.sec; dir_entry->CreateTimestamp.MilliSecond = 0; @@ -1926,7 +1926,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->ModifyTimestamp.year = tm.year; dir_entry->ModifyTimestamp.month = tm.mon; dir_entry->ModifyTimestamp.day = tm.day; - dir_entry->ModifyTimestamp.Hour = tm.hour; + dir_entry->ModifyTimestamp.hour = tm.hour; dir_entry->ModifyTimestamp.Minute = tm.min; dir_entry->ModifyTimestamp.Second = tm.sec; dir_entry->ModifyTimestamp.MilliSecond = 0; From 56a2ba5a228a378ba8ead7e704017dab9b3883f2 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:44 +0530 Subject: [PATCH 0108/2231] staging: exfat: Rename variable 'Minute' to 'minute' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "Minute" to "minute" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-6-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index b085bf51688c..68943e08f518 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -226,7 +226,7 @@ struct date_time_t { u16 month; u16 day; u16 hour; - u16 Minute; + u16 minute; u16 Second; u16 MilliSecond; }; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index d83c485c5fb4..4425ef2b444f 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -60,7 +60,7 @@ static void exfat_write_super(struct super_block *sb); static void exfat_time_fat2unix(struct timespec64 *ts, struct date_time_t *tp) { ts->tv_sec = mktime64(tp->year + 1980, tp->month + 1, tp->day, - tp->hour, tp->Minute, tp->Second); + tp->hour, tp->minute, tp->Second); ts->tv_nsec = tp->MilliSecond * NSEC_PER_MSEC; } @@ -76,7 +76,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) if (second < UNIX_SECS_1980) { tp->MilliSecond = 0; tp->Second = 0; - tp->Minute = 0; + tp->minute = 0; tp->hour = 0; tp->day = 1; tp->month = 1; @@ -87,7 +87,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) if (second >= UNIX_SECS_2108) { tp->MilliSecond = 999; tp->Second = 59; - tp->Minute = 59; + tp->minute = 59; tp->hour = 23; tp->day = 31; tp->month = 12; @@ -97,7 +97,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) tp->MilliSecond = ts->tv_nsec / NSEC_PER_MSEC; tp->Second = tm.tm_sec; - tp->Minute = tm.tm_min; + tp->minute = tm.tm_min; tp->hour = tm.tm_hour; tp->day = tm.tm_mday; tp->month = tm.tm_mon + 1; @@ -1501,7 +1501,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->CreateTimestamp.month = tm.mon; info->CreateTimestamp.day = tm.day; info->CreateTimestamp.hour = tm.hour; - info->CreateTimestamp.Minute = tm.min; + info->CreateTimestamp.minute = tm.min; info->CreateTimestamp.Second = tm.sec; info->CreateTimestamp.MilliSecond = 0; @@ -1510,7 +1510,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->ModifyTimestamp.month = tm.mon; info->ModifyTimestamp.day = tm.day; info->ModifyTimestamp.hour = tm.hour; - info->ModifyTimestamp.Minute = tm.min; + info->ModifyTimestamp.minute = tm.min; info->ModifyTimestamp.Second = tm.sec; info->ModifyTimestamp.MilliSecond = 0; @@ -1598,7 +1598,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) /* set FILE_INFO structure using the acquired struct dentry_t */ tm.sec = info->CreateTimestamp.Second; - tm.min = info->CreateTimestamp.Minute; + tm.min = info->CreateTimestamp.minute; tm.hour = info->CreateTimestamp.hour; tm.day = info->CreateTimestamp.day; tm.mon = info->CreateTimestamp.month; @@ -1606,7 +1606,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) exfat_set_entry_time(ep, &tm, TM_CREATE); tm.sec = info->ModifyTimestamp.Second; - tm.min = info->ModifyTimestamp.Minute; + tm.min = info->ModifyTimestamp.minute; tm.hour = info->ModifyTimestamp.hour; tm.day = info->ModifyTimestamp.day; tm.mon = info->ModifyTimestamp.month; @@ -1918,7 +1918,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->CreateTimestamp.month = tm.mon; dir_entry->CreateTimestamp.day = tm.day; dir_entry->CreateTimestamp.hour = tm.hour; - dir_entry->CreateTimestamp.Minute = tm.min; + dir_entry->CreateTimestamp.minute = tm.min; dir_entry->CreateTimestamp.Second = tm.sec; dir_entry->CreateTimestamp.MilliSecond = 0; @@ -1927,7 +1927,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->ModifyTimestamp.month = tm.mon; dir_entry->ModifyTimestamp.day = tm.day; dir_entry->ModifyTimestamp.hour = tm.hour; - dir_entry->ModifyTimestamp.Minute = tm.min; + dir_entry->ModifyTimestamp.minute = tm.min; dir_entry->ModifyTimestamp.Second = tm.sec; dir_entry->ModifyTimestamp.MilliSecond = 0; From ac4db9acc5d5cdc49b996ed75613b63f670a1ad6 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:45 +0530 Subject: [PATCH 0109/2231] staging: exfat: Rename variable 'Second' to 'second' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "Second" to "second" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-7-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 68943e08f518..ec9c8fa4274b 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -227,7 +227,7 @@ struct date_time_t { u16 day; u16 hour; u16 minute; - u16 Second; + u16 second; u16 MilliSecond; }; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 4425ef2b444f..7b8e909d62f4 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -60,7 +60,7 @@ static void exfat_write_super(struct super_block *sb); static void exfat_time_fat2unix(struct timespec64 *ts, struct date_time_t *tp) { ts->tv_sec = mktime64(tp->year + 1980, tp->month + 1, tp->day, - tp->hour, tp->minute, tp->Second); + tp->hour, tp->minute, tp->second); ts->tv_nsec = tp->MilliSecond * NSEC_PER_MSEC; } @@ -75,7 +75,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) if (second < UNIX_SECS_1980) { tp->MilliSecond = 0; - tp->Second = 0; + tp->second = 0; tp->minute = 0; tp->hour = 0; tp->day = 1; @@ -86,7 +86,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) if (second >= UNIX_SECS_2108) { tp->MilliSecond = 999; - tp->Second = 59; + tp->second = 59; tp->minute = 59; tp->hour = 23; tp->day = 31; @@ -96,7 +96,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) } tp->MilliSecond = ts->tv_nsec / NSEC_PER_MSEC; - tp->Second = tm.tm_sec; + tp->second = tm.tm_sec; tp->minute = tm.tm_min; tp->hour = tm.tm_hour; tp->day = tm.tm_mday; @@ -1502,7 +1502,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->CreateTimestamp.day = tm.day; info->CreateTimestamp.hour = tm.hour; info->CreateTimestamp.minute = tm.min; - info->CreateTimestamp.Second = tm.sec; + info->CreateTimestamp.second = tm.sec; info->CreateTimestamp.MilliSecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); @@ -1511,7 +1511,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->ModifyTimestamp.day = tm.day; info->ModifyTimestamp.hour = tm.hour; info->ModifyTimestamp.minute = tm.min; - info->ModifyTimestamp.Second = tm.sec; + info->ModifyTimestamp.second = tm.sec; info->ModifyTimestamp.MilliSecond = 0; memset((char *)&info->AccessTimestamp, 0, sizeof(struct date_time_t)); @@ -1597,7 +1597,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) exfat_set_entry_attr(ep, info->Attr); /* set FILE_INFO structure using the acquired struct dentry_t */ - tm.sec = info->CreateTimestamp.Second; + tm.sec = info->CreateTimestamp.second; tm.min = info->CreateTimestamp.minute; tm.hour = info->CreateTimestamp.hour; tm.day = info->CreateTimestamp.day; @@ -1605,7 +1605,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.year = info->CreateTimestamp.year; exfat_set_entry_time(ep, &tm, TM_CREATE); - tm.sec = info->ModifyTimestamp.Second; + tm.sec = info->ModifyTimestamp.second; tm.min = info->ModifyTimestamp.minute; tm.hour = info->ModifyTimestamp.hour; tm.day = info->ModifyTimestamp.day; @@ -1919,7 +1919,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->CreateTimestamp.day = tm.day; dir_entry->CreateTimestamp.hour = tm.hour; dir_entry->CreateTimestamp.minute = tm.min; - dir_entry->CreateTimestamp.Second = tm.sec; + dir_entry->CreateTimestamp.second = tm.sec; dir_entry->CreateTimestamp.MilliSecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); @@ -1928,7 +1928,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->ModifyTimestamp.day = tm.day; dir_entry->ModifyTimestamp.hour = tm.hour; dir_entry->ModifyTimestamp.minute = tm.min; - dir_entry->ModifyTimestamp.Second = tm.sec; + dir_entry->ModifyTimestamp.second = tm.sec; dir_entry->ModifyTimestamp.MilliSecond = 0; memset((char *)&dir_entry->AccessTimestamp, 0, From 36b392b0423a117fe5f6137ad8a6f792284fde90 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:46 +0530 Subject: [PATCH 0110/2231] staging: exfat: Rename variable 'MilliSecond' to 'millisecond' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "MilliSecond" to "millisecond" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-8-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index ec9c8fa4274b..f9846ae2aa19 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -228,7 +228,7 @@ struct date_time_t { u16 hour; u16 minute; u16 second; - u16 MilliSecond; + u16 millisecond; }; struct vol_info_t { diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 7b8e909d62f4..1aa08765f107 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -62,7 +62,7 @@ static void exfat_time_fat2unix(struct timespec64 *ts, struct date_time_t *tp) ts->tv_sec = mktime64(tp->year + 1980, tp->month + 1, tp->day, tp->hour, tp->minute, tp->second); - ts->tv_nsec = tp->MilliSecond * NSEC_PER_MSEC; + ts->tv_nsec = tp->millisecond * NSEC_PER_MSEC; } /* Convert linear UNIX date to a FAT time/date pair. */ @@ -74,7 +74,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) time64_to_tm(second, 0, &tm); if (second < UNIX_SECS_1980) { - tp->MilliSecond = 0; + tp->millisecond = 0; tp->second = 0; tp->minute = 0; tp->hour = 0; @@ -85,7 +85,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) } if (second >= UNIX_SECS_2108) { - tp->MilliSecond = 999; + tp->millisecond = 999; tp->second = 59; tp->minute = 59; tp->hour = 23; @@ -95,7 +95,7 @@ static void exfat_time_unix2fat(struct timespec64 *ts, struct date_time_t *tp) return; } - tp->MilliSecond = ts->tv_nsec / NSEC_PER_MSEC; + tp->millisecond = ts->tv_nsec / NSEC_PER_MSEC; tp->second = tm.tm_sec; tp->minute = tm.tm_min; tp->hour = tm.tm_hour; @@ -1503,7 +1503,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->CreateTimestamp.hour = tm.hour; info->CreateTimestamp.minute = tm.min; info->CreateTimestamp.second = tm.sec; - info->CreateTimestamp.MilliSecond = 0; + info->CreateTimestamp.millisecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); info->ModifyTimestamp.year = tm.year; @@ -1512,7 +1512,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->ModifyTimestamp.hour = tm.hour; info->ModifyTimestamp.minute = tm.min; info->ModifyTimestamp.second = tm.sec; - info->ModifyTimestamp.MilliSecond = 0; + info->ModifyTimestamp.millisecond = 0; memset((char *)&info->AccessTimestamp, 0, sizeof(struct date_time_t)); @@ -1920,7 +1920,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->CreateTimestamp.hour = tm.hour; dir_entry->CreateTimestamp.minute = tm.min; dir_entry->CreateTimestamp.second = tm.sec; - dir_entry->CreateTimestamp.MilliSecond = 0; + dir_entry->CreateTimestamp.millisecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); dir_entry->ModifyTimestamp.year = tm.year; @@ -1929,7 +1929,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->ModifyTimestamp.hour = tm.hour; dir_entry->ModifyTimestamp.minute = tm.min; dir_entry->ModifyTimestamp.second = tm.sec; - dir_entry->ModifyTimestamp.MilliSecond = 0; + dir_entry->ModifyTimestamp.millisecond = 0; memset((char *)&dir_entry->AccessTimestamp, 0, sizeof(struct date_time_t)); From 731e7914190b33e3ab752821ffc240e95c9cea9d Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:52 +0530 Subject: [PATCH 0111/2231] staging: exfat: Rename variable 'Name' to 'name' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "Name" to "name" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-14-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index f9846ae2aa19..aab3d850f1b9 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -260,7 +260,7 @@ struct file_id_t { }; struct dir_entry_t { - char Name[MAX_NAME_LENGTH * MAX_CHARSET_SIZE]; + char name[MAX_NAME_LENGTH * MAX_CHARSET_SIZE]; /* used only for FAT12/16/32, not used for exFAT */ char ShortName[DOS_NAME_LENGTH + 2]; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 1aa08765f107..cbb9b02e5515 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1457,7 +1457,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) memset((char *)&info->AccessTimestamp, 0, sizeof(struct date_time_t)); strcpy(info->ShortName, "."); - strcpy(info->Name, "."); + strcpy(info->name, "."); dir.dir = p_fs->root_dir; dir.flags = 0x01; @@ -1522,7 +1522,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) */ exfat_get_uni_name_from_ext_entry(sb, &fid->dir, fid->entry, uni_name.name); - nls_uniname_to_cstring(sb, info->Name, &uni_name); + nls_uniname_to_cstring(sb, info->name, &uni_name); info->NumSubdirs = 2; @@ -1937,7 +1937,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) *uni_name.name = 0x0; exfat_get_uni_name_from_ext_entry(sb, &dir, dentry, uni_name.name); - nls_uniname_to_cstring(sb, dir_entry->Name, &uni_name); + nls_uniname_to_cstring(sb, dir_entry->name, &uni_name); exfat_buf_unlock(sb, sector); ep = get_entry_in_dir(sb, &clu, i + 1, NULL); @@ -1980,7 +1980,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) } } - *dir_entry->Name = '\0'; + *dir_entry->name = '\0'; fid->rwoffset = (s64)(++dentry); @@ -2108,7 +2108,7 @@ get_new: cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; - if (!de.Name[0]) + if (!de.name[0]) goto end_of_dir; if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { @@ -2128,7 +2128,7 @@ get_new: } } - if (!dir_emit(ctx, de.Name, strlen(de.Name), inum, + if (!dir_emit(ctx, de.name, strlen(de.name), inum, (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) goto out; From cc3edc28b9adf4a31895aefc49e610e3249bc7a7 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:53 +0530 Subject: [PATCH 0112/2231] staging: exfat: Rename variable 'ShortName' to 'short_name' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "ShortName" to "short_name" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-15-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index aab3d850f1b9..5deb6667bf77 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -263,7 +263,7 @@ struct dir_entry_t { char name[MAX_NAME_LENGTH * MAX_CHARSET_SIZE]; /* used only for FAT12/16/32, not used for exFAT */ - char ShortName[DOS_NAME_LENGTH + 2]; + char short_name[DOS_NAME_LENGTH + 2]; u32 Attr; u64 Size; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index cbb9b02e5515..7409ddc39e1f 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1456,7 +1456,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) sizeof(struct date_time_t)); memset((char *)&info->AccessTimestamp, 0, sizeof(struct date_time_t)); - strcpy(info->ShortName, "."); + strcpy(info->short_name, "."); strcpy(info->name, "."); dir.dir = p_fs->root_dir; @@ -2111,9 +2111,9 @@ get_new: if (!de.name[0]) goto end_of_dir; - if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + if (!memcmp(de.short_name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { inum = inode->i_ino; - } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + } else if (!memcmp(de.short_name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { inum = parent_ino(filp->f_path.dentry); } else { loff_t i_pos = ((loff_t)EXFAT_I(inode)->fid.start_clu << 32) | From a474b9021ef48449e0b513bc4ac20685dbf45773 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:54 +0530 Subject: [PATCH 0113/2231] staging: exfat: Rename variable 'Attr' to 'attr' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "Attr" to "attr" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-16-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 5deb6667bf77..e8e6412a8c11 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -265,7 +265,7 @@ struct dir_entry_t { /* used only for FAT12/16/32, not used for exFAT */ char short_name[DOS_NAME_LENGTH + 2]; - u32 Attr; + u32 attr; u64 Size; u32 NumSubdirs; struct date_time_t CreateTimestamp; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 7409ddc39e1f..a134b13525f4 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1449,7 +1449,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) if (is_dir) { if ((fid->dir.dir == p_fs->root_dir) && (fid->entry == -1)) { - info->Attr = ATTR_SUBDIR; + info->attr = ATTR_SUBDIR; memset((char *)&info->CreateTimestamp, 0, sizeof(struct date_time_t)); memset((char *)&info->ModifyTimestamp, 0, @@ -1494,7 +1494,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) ep2 = ep + 1; /* set FILE_INFO structure using the acquired struct dentry_t */ - info->Attr = exfat_get_entry_attr(ep); + info->attr = exfat_get_entry_attr(ep); exfat_get_entry_time(ep, &tm, TM_CREATE); info->CreateTimestamp.year = tm.year; @@ -1594,7 +1594,7 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) } ep2 = ep + 1; - exfat_set_entry_attr(ep, info->Attr); + exfat_set_entry_attr(ep, info->attr); /* set FILE_INFO structure using the acquired struct dentry_t */ tm.sec = info->CreateTimestamp.second; @@ -1911,7 +1911,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) continue; exfat_buf_lock(sb, sector); - dir_entry->Attr = exfat_get_entry_attr(ep); + dir_entry->attr = exfat_get_entry_attr(ep); exfat_get_entry_time(ep, &tm, TM_CREATE); dir_entry->CreateTimestamp.year = tm.year; @@ -2129,7 +2129,7 @@ get_new: } if (!dir_emit(ctx, de.name, strlen(de.name), inum, - (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) + (de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) goto out; ctx->pos = cpos; @@ -3138,25 +3138,25 @@ static int exfat_fill_inode(struct inode *inode, struct file_id_t *fid) INC_IVERSION(inode); inode->i_generation = prandom_u32(); - if (info.Attr & ATTR_SUBDIR) { /* directory */ + if (info.attr & ATTR_SUBDIR) { /* directory */ inode->i_generation &= ~1; - inode->i_mode = exfat_make_mode(sbi, info.Attr, 0777); + inode->i_mode = exfat_make_mode(sbi, info.attr, 0777); inode->i_op = &exfat_dir_inode_operations; inode->i_fop = &exfat_dir_operations; i_size_write(inode, info.Size); EXFAT_I(inode)->mmu_private = i_size_read(inode); set_nlink(inode, info.NumSubdirs); - } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */ + } else if (info.attr & ATTR_SYMLINK) { /* symbolic link */ inode->i_generation |= 1; - inode->i_mode = exfat_make_mode(sbi, info.Attr, 0777); + inode->i_mode = exfat_make_mode(sbi, info.attr, 0777); inode->i_op = &exfat_symlink_inode_operations; i_size_write(inode, info.Size); EXFAT_I(inode)->mmu_private = i_size_read(inode); } else { /* regular file */ inode->i_generation |= 1; - inode->i_mode = exfat_make_mode(sbi, info.Attr, 0777); + inode->i_mode = exfat_make_mode(sbi, info.attr, 0777); inode->i_op = &exfat_file_inode_operations; inode->i_fop = &exfat_file_operations; inode->i_mapping->a_ops = &exfat_aops; @@ -3165,7 +3165,7 @@ static int exfat_fill_inode(struct inode *inode, struct file_id_t *fid) i_size_write(inode, info.Size); EXFAT_I(inode)->mmu_private = i_size_read(inode); } - exfat_save_attr(inode, info.Attr); + exfat_save_attr(inode, info.attr); inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) & ~((loff_t)p_fs->cluster_size - 1)) >> 9; @@ -3238,7 +3238,7 @@ static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) if (inode->i_ino == EXFAT_ROOT_INO) return 0; - info.Attr = exfat_make_attr(inode); + info.attr = exfat_make_attr(inode); info.Size = i_size_read(inode); exfat_time_unix2fat(&inode->i_mtime, &info.ModifyTimestamp); From 439968b7bb61f3f587658e18f00e9b25bf458445 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:55 +0530 Subject: [PATCH 0114/2231] staging: exfat: Rename variable 'NumSubdirs' to 'num_subdirs' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "NumSubdirs" to "num_subdirs" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-17-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index e8e6412a8c11..40362c419403 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -267,7 +267,7 @@ struct dir_entry_t { u32 attr; u64 Size; - u32 NumSubdirs; + u32 num_subdirs; struct date_time_t CreateTimestamp; struct date_time_t ModifyTimestamp; struct date_time_t AccessTimestamp; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index a134b13525f4..1bb0a29701ad 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1476,7 +1476,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) ret = count; /* propagate error upward */ goto out; } - info->NumSubdirs = count; + info->num_subdirs = count; if (p_fs->dev_ejected) ret = -EIO; @@ -1524,7 +1524,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) uni_name.name); nls_uniname_to_cstring(sb, info->name, &uni_name); - info->NumSubdirs = 2; + info->num_subdirs = 2; info->Size = exfat_get_entry_size(ep2); @@ -1543,7 +1543,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) ret = count; /* propagate error upward */ goto out; } - info->NumSubdirs += count; + info->num_subdirs += count; } if (p_fs->dev_ejected) @@ -3146,7 +3146,7 @@ static int exfat_fill_inode(struct inode *inode, struct file_id_t *fid) i_size_write(inode, info.Size); EXFAT_I(inode)->mmu_private = i_size_read(inode); - set_nlink(inode, info.NumSubdirs); + set_nlink(inode, info.num_subdirs); } else if (info.attr & ATTR_SYMLINK) { /* symbolic link */ inode->i_generation |= 1; inode->i_mode = exfat_make_mode(sbi, info.attr, 0777); @@ -3646,7 +3646,7 @@ static int exfat_read_root(struct inode *inode) inode->i_mtime = curtime; inode->i_atime = curtime; inode->i_ctime = curtime; - set_nlink(inode, info.NumSubdirs + 2); + set_nlink(inode, info.num_subdirs + 2); return 0; } From d9cd17a258c3f05799c7322fe1afd859a42994fe Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:56 +0530 Subject: [PATCH 0115/2231] staging: exfat: Rename variable 'CreateTimestamp' to 'create_timestamp' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "CreateTimestamp" to "create_timestamp" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-18-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 46 ++++++++++++++--------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 40362c419403..3b7585f9b861 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -268,7 +268,7 @@ struct dir_entry_t { u32 attr; u64 Size; u32 num_subdirs; - struct date_time_t CreateTimestamp; + struct date_time_t create_timestamp; struct date_time_t ModifyTimestamp; struct date_time_t AccessTimestamp; }; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 1bb0a29701ad..6280f5b5a200 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1450,7 +1450,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) if ((fid->dir.dir == p_fs->root_dir) && (fid->entry == -1)) { info->attr = ATTR_SUBDIR; - memset((char *)&info->CreateTimestamp, 0, + memset((char *)&info->create_timestamp, 0, sizeof(struct date_time_t)); memset((char *)&info->ModifyTimestamp, 0, sizeof(struct date_time_t)); @@ -1497,13 +1497,13 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->attr = exfat_get_entry_attr(ep); exfat_get_entry_time(ep, &tm, TM_CREATE); - info->CreateTimestamp.year = tm.year; - info->CreateTimestamp.month = tm.mon; - info->CreateTimestamp.day = tm.day; - info->CreateTimestamp.hour = tm.hour; - info->CreateTimestamp.minute = tm.min; - info->CreateTimestamp.second = tm.sec; - info->CreateTimestamp.millisecond = 0; + info->create_timestamp.year = tm.year; + info->create_timestamp.month = tm.mon; + info->create_timestamp.day = tm.day; + info->create_timestamp.hour = tm.hour; + info->create_timestamp.minute = tm.min; + info->create_timestamp.second = tm.sec; + info->create_timestamp.millisecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); info->ModifyTimestamp.year = tm.year; @@ -1597,12 +1597,12 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) exfat_set_entry_attr(ep, info->attr); /* set FILE_INFO structure using the acquired struct dentry_t */ - tm.sec = info->CreateTimestamp.second; - tm.min = info->CreateTimestamp.minute; - tm.hour = info->CreateTimestamp.hour; - tm.day = info->CreateTimestamp.day; - tm.mon = info->CreateTimestamp.month; - tm.year = info->CreateTimestamp.year; + tm.sec = info->create_timestamp.second; + tm.min = info->create_timestamp.minute; + tm.hour = info->create_timestamp.hour; + tm.day = info->create_timestamp.day; + tm.mon = info->create_timestamp.month; + tm.year = info->create_timestamp.year; exfat_set_entry_time(ep, &tm, TM_CREATE); tm.sec = info->ModifyTimestamp.second; @@ -1914,13 +1914,13 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->attr = exfat_get_entry_attr(ep); exfat_get_entry_time(ep, &tm, TM_CREATE); - dir_entry->CreateTimestamp.year = tm.year; - dir_entry->CreateTimestamp.month = tm.mon; - dir_entry->CreateTimestamp.day = tm.day; - dir_entry->CreateTimestamp.hour = tm.hour; - dir_entry->CreateTimestamp.minute = tm.min; - dir_entry->CreateTimestamp.second = tm.sec; - dir_entry->CreateTimestamp.millisecond = 0; + dir_entry->create_timestamp.year = tm.year; + dir_entry->create_timestamp.month = tm.mon; + dir_entry->create_timestamp.day = tm.day; + dir_entry->create_timestamp.hour = tm.hour; + dir_entry->create_timestamp.minute = tm.min; + dir_entry->create_timestamp.second = tm.sec; + dir_entry->create_timestamp.millisecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); dir_entry->ModifyTimestamp.year = tm.year; @@ -3171,7 +3171,7 @@ static int exfat_fill_inode(struct inode *inode, struct file_id_t *fid) & ~((loff_t)p_fs->cluster_size - 1)) >> 9; exfat_time_fat2unix(&inode->i_mtime, &info.ModifyTimestamp); - exfat_time_fat2unix(&inode->i_ctime, &info.CreateTimestamp); + exfat_time_fat2unix(&inode->i_ctime, &info.create_timestamp); exfat_time_fat2unix(&inode->i_atime, &info.AccessTimestamp); return 0; @@ -3242,7 +3242,7 @@ static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) info.Size = i_size_read(inode); exfat_time_unix2fat(&inode->i_mtime, &info.ModifyTimestamp); - exfat_time_unix2fat(&inode->i_ctime, &info.CreateTimestamp); + exfat_time_unix2fat(&inode->i_ctime, &info.create_timestamp); exfat_time_unix2fat(&inode->i_atime, &info.AccessTimestamp); ffsWriteStat(inode, &info); From c09c0adc20cd2fe9cf4ad45e245bfc8f14b5ae62 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:57 +0530 Subject: [PATCH 0116/2231] staging: exfat: Rename variable 'ModifyTimestamp' to 'modify_timestamp' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "ModifyTimestamp" to "modify_timestamp" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-19-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 46 ++++++++++++++--------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 3b7585f9b861..19bfa10da807 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -269,7 +269,7 @@ struct dir_entry_t { u64 Size; u32 num_subdirs; struct date_time_t create_timestamp; - struct date_time_t ModifyTimestamp; + struct date_time_t modify_timestamp; struct date_time_t AccessTimestamp; }; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 6280f5b5a200..ac4f4a13b910 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1452,7 +1452,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->attr = ATTR_SUBDIR; memset((char *)&info->create_timestamp, 0, sizeof(struct date_time_t)); - memset((char *)&info->ModifyTimestamp, 0, + memset((char *)&info->modify_timestamp, 0, sizeof(struct date_time_t)); memset((char *)&info->AccessTimestamp, 0, sizeof(struct date_time_t)); @@ -1506,13 +1506,13 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->create_timestamp.millisecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); - info->ModifyTimestamp.year = tm.year; - info->ModifyTimestamp.month = tm.mon; - info->ModifyTimestamp.day = tm.day; - info->ModifyTimestamp.hour = tm.hour; - info->ModifyTimestamp.minute = tm.min; - info->ModifyTimestamp.second = tm.sec; - info->ModifyTimestamp.millisecond = 0; + info->modify_timestamp.year = tm.year; + info->modify_timestamp.month = tm.mon; + info->modify_timestamp.day = tm.day; + info->modify_timestamp.hour = tm.hour; + info->modify_timestamp.minute = tm.min; + info->modify_timestamp.second = tm.sec; + info->modify_timestamp.millisecond = 0; memset((char *)&info->AccessTimestamp, 0, sizeof(struct date_time_t)); @@ -1605,12 +1605,12 @@ static int ffsWriteStat(struct inode *inode, struct dir_entry_t *info) tm.year = info->create_timestamp.year; exfat_set_entry_time(ep, &tm, TM_CREATE); - tm.sec = info->ModifyTimestamp.second; - tm.min = info->ModifyTimestamp.minute; - tm.hour = info->ModifyTimestamp.hour; - tm.day = info->ModifyTimestamp.day; - tm.mon = info->ModifyTimestamp.month; - tm.year = info->ModifyTimestamp.year; + tm.sec = info->modify_timestamp.second; + tm.min = info->modify_timestamp.minute; + tm.hour = info->modify_timestamp.hour; + tm.day = info->modify_timestamp.day; + tm.mon = info->modify_timestamp.month; + tm.year = info->modify_timestamp.year; exfat_set_entry_time(ep, &tm, TM_MODIFY); exfat_set_entry_size(ep2, info->Size); @@ -1923,13 +1923,13 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->create_timestamp.millisecond = 0; exfat_get_entry_time(ep, &tm, TM_MODIFY); - dir_entry->ModifyTimestamp.year = tm.year; - dir_entry->ModifyTimestamp.month = tm.mon; - dir_entry->ModifyTimestamp.day = tm.day; - dir_entry->ModifyTimestamp.hour = tm.hour; - dir_entry->ModifyTimestamp.minute = tm.min; - dir_entry->ModifyTimestamp.second = tm.sec; - dir_entry->ModifyTimestamp.millisecond = 0; + dir_entry->modify_timestamp.year = tm.year; + dir_entry->modify_timestamp.month = tm.mon; + dir_entry->modify_timestamp.day = tm.day; + dir_entry->modify_timestamp.hour = tm.hour; + dir_entry->modify_timestamp.minute = tm.min; + dir_entry->modify_timestamp.second = tm.sec; + dir_entry->modify_timestamp.millisecond = 0; memset((char *)&dir_entry->AccessTimestamp, 0, sizeof(struct date_time_t)); @@ -3170,7 +3170,7 @@ static int exfat_fill_inode(struct inode *inode, struct file_id_t *fid) inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) & ~((loff_t)p_fs->cluster_size - 1)) >> 9; - exfat_time_fat2unix(&inode->i_mtime, &info.ModifyTimestamp); + exfat_time_fat2unix(&inode->i_mtime, &info.modify_timestamp); exfat_time_fat2unix(&inode->i_ctime, &info.create_timestamp); exfat_time_fat2unix(&inode->i_atime, &info.AccessTimestamp); @@ -3241,7 +3241,7 @@ static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) info.attr = exfat_make_attr(inode); info.Size = i_size_read(inode); - exfat_time_unix2fat(&inode->i_mtime, &info.ModifyTimestamp); + exfat_time_unix2fat(&inode->i_mtime, &info.modify_timestamp); exfat_time_unix2fat(&inode->i_ctime, &info.create_timestamp); exfat_time_unix2fat(&inode->i_atime, &info.AccessTimestamp); From 2ed9d77aa76b112a526ddde4c07857f4d7305808 Mon Sep 17 00:00:00 2001 From: Pragat Pandya Date: Tue, 11 Feb 2020 00:05:58 +0530 Subject: [PATCH 0117/2231] staging: exfat: Rename variable 'AccessTimestamp' to 'access_timestamp' Fix checkpatch warning: Avoid CamelCase Change all occurrences of identifier "AccessTimestamp" to "access_timestamp" Signed-off-by: Pragat Pandya Link: https://lore.kernel.org/r/20200210183558.11836-20-pragat.pandya@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_super.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 19bfa10da807..05e21839b349 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -270,7 +270,7 @@ struct dir_entry_t { u32 num_subdirs; struct date_time_t create_timestamp; struct date_time_t modify_timestamp; - struct date_time_t AccessTimestamp; + struct date_time_t access_timestamp; }; struct timestamp_t { diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index ac4f4a13b910..8446444cf68a 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1454,7 +1454,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) sizeof(struct date_time_t)); memset((char *)&info->modify_timestamp, 0, sizeof(struct date_time_t)); - memset((char *)&info->AccessTimestamp, 0, + memset((char *)&info->access_timestamp, 0, sizeof(struct date_time_t)); strcpy(info->short_name, "."); strcpy(info->name, "."); @@ -1514,7 +1514,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->modify_timestamp.second = tm.sec; info->modify_timestamp.millisecond = 0; - memset((char *)&info->AccessTimestamp, 0, sizeof(struct date_time_t)); + memset((char *)&info->access_timestamp, 0, sizeof(struct date_time_t)); *uni_name.name = 0x0; /* XXX this is very bad for exfat cuz name is already included in es. @@ -1931,7 +1931,7 @@ static int ffsReadDir(struct inode *inode, struct dir_entry_t *dir_entry) dir_entry->modify_timestamp.second = tm.sec; dir_entry->modify_timestamp.millisecond = 0; - memset((char *)&dir_entry->AccessTimestamp, 0, + memset((char *)&dir_entry->access_timestamp, 0, sizeof(struct date_time_t)); *uni_name.name = 0x0; @@ -3172,7 +3172,7 @@ static int exfat_fill_inode(struct inode *inode, struct file_id_t *fid) exfat_time_fat2unix(&inode->i_mtime, &info.modify_timestamp); exfat_time_fat2unix(&inode->i_ctime, &info.create_timestamp); - exfat_time_fat2unix(&inode->i_atime, &info.AccessTimestamp); + exfat_time_fat2unix(&inode->i_atime, &info.access_timestamp); return 0; } @@ -3243,7 +3243,7 @@ static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) exfat_time_unix2fat(&inode->i_mtime, &info.modify_timestamp); exfat_time_unix2fat(&inode->i_ctime, &info.create_timestamp); - exfat_time_unix2fat(&inode->i_atime, &info.AccessTimestamp); + exfat_time_unix2fat(&inode->i_atime, &info.access_timestamp); ffsWriteStat(inode, &info); From 1f9d6a3d2ebcf1791f4e0f2eb7c96f2197e2205a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 15:17:22 -0600 Subject: [PATCH 0118/2231] staging: unisys: visorinput: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200211211722.GA1640@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/staging/unisys/visorinput/visorinput.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/unisys/visorinput/visorinput.c b/drivers/staging/unisys/visorinput/visorinput.c index 9693fb559052..6d202cba8575 100644 --- a/drivers/staging/unisys/visorinput/visorinput.c +++ b/drivers/staging/unisys/visorinput/visorinput.c @@ -111,7 +111,7 @@ struct visorinput_devdata { /* size of following array */ unsigned int keycode_table_bytes; /* for keyboard devices: visorkbd_keycode[] + visorkbd_ext_keycode[] */ - unsigned char keycode_table[0]; + unsigned char keycode_table[]; }; static const guid_t visor_keyboard_channel_guid = VISOR_KEYBOARD_CHANNEL_GUID; From 8acd7c56c1b502b745c7b0dc341abcd1415ba5ad Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 15:12:19 -0600 Subject: [PATCH 0119/2231] staging: greybus: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200211211219.GA673@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index 64a17dfe3b6e..2a375f407d38 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -29,7 +29,7 @@ struct gb_raw { struct raw_data { struct list_head entry; u32 len; - u8 data[0]; + u8 data[]; }; static struct class *raw_class; From 40ab82e08d789d8f4437fec63142b40120c1c50f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:40:54 +0100 Subject: [PATCH 0120/2231] intel_idle: Simplify LAPIC timer reliability checks The lapic_timer_always_reliable variable really takes only two values and some arithmetic in intel_idle() related to comparing it with the target C-state's MWAIT hint value is unnecessary. Simplify the code by replacing lapic_timer_always_reliable with a bool variable lapic_timer_always_reliable and dropping the LAPIC_TIMER_ALWAYS_RELIABLE symbol along with the excess computations in intel_idle(). While at it, add a comment explaining the branch taken in intel_idle() if the LAPIC timer is only reliable in C1 and modify the related debug message in intel_idle_init() accordingly (the modification of this message in the only expected functional impact of the change made here). Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index d55606608ac8..8d66efc53b89 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -66,10 +66,7 @@ static int max_cstate = CPUIDLE_STATE_MAX - 1; static unsigned int disabled_states_mask; static unsigned int mwait_substates; - -#define LAPIC_TIMER_ALWAYS_RELIABLE 0xFFFFFFFF -/* Reliable LAPIC Timer States, bit 1 for C1 etc. */ -static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ +static bool lapic_timer_always_reliable; struct idle_cpu { struct cpuidle_state *state_table; @@ -908,7 +905,6 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev, unsigned long ecx = 1; /* break on interrupt flag */ struct cpuidle_state *state = &drv->states[index]; unsigned long eax = flg2MWAIT(state->flags); - unsigned int cstate; bool uninitialized_var(tick); int cpu = smp_processor_id(); @@ -919,13 +915,16 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev, if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED) leave_mm(cpu); - if (!static_cpu_has(X86_FEATURE_ARAT)) { - cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) & - MWAIT_CSTATE_MASK) + 1; - tick = false; - if (!(lapic_timer_reliable_states & (1 << (cstate)))) { + if (!static_cpu_has(X86_FEATURE_ARAT) && !lapic_timer_always_reliable) { + /* + * Switch over to one-shot tick broadcast if the target C-state + * is deeper than C1. + */ + if ((eax >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) { tick = true; tick_broadcast_enter(); + } else { + tick = false; } } @@ -1555,7 +1554,7 @@ static int intel_idle_cpu_online(unsigned int cpu) { struct cpuidle_device *dev; - if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) + if (!lapic_timer_always_reliable) tick_broadcast_enable(); /* @@ -1647,15 +1646,15 @@ static int __init intel_idle_init(void) } if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ - lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; + lapic_timer_always_reliable = true; retval = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "idle/intel:online", intel_idle_cpu_online, NULL); if (retval < 0) goto hp_setup_fail; - pr_debug("lapic_timer_reliable_states 0x%x\n", - lapic_timer_reliable_states); + pr_debug("Local APIC timer is reliable in %s\n", + lapic_timer_always_reliable ? "all C-states" : "C1"); return 0; From bc721c1e4517a5110c4b0ae2e80345d3db6425fc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:41:06 +0100 Subject: [PATCH 0121/2231] intel_idle: Clean up definitions of cpuidle callbacks Add proper kerneldoc descriptions to intel_idle() and intel_idle_s2idle(), annotate the latter with __cpuidle and reorder the declarations of local variables in both of them to reflect the mwait_idle_with_hints() arguments order. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 8d66efc53b89..5adc058c705d 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -892,19 +892,28 @@ static struct cpuidle_state dnv_cstates[] = { }; /** - * intel_idle - * @dev: cpuidle_device - * @drv: cpuidle driver - * @index: index of cpuidle state + * intel_idle - Ask the processor to enter the given idle state. + * @dev: cpuidle device of the target CPU. + * @drv: cpuidle driver (assumed to point to intel_idle_driver). + * @index: Target idle state index. + * + * Use the MWAIT instruction to notify the processor that the CPU represented by + * @dev is idle and it can try to enter the idle state corresponding to @index. + * + * If the local APIC timer is not known to be reliable in the target idle state, + * enable one-shot tick broadcasting for the target CPU before executing MWAIT. + * + * Optionally call leave_mm() for the target CPU upfront to avoid wakeups due to + * flushing user TLBs. * * Must be called under local_irq_disable(). */ static __cpuidle int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { - unsigned long ecx = 1; /* break on interrupt flag */ struct cpuidle_state *state = &drv->states[index]; unsigned long eax = flg2MWAIT(state->flags); + unsigned long ecx = 1; /* break on interrupt flag */ bool uninitialized_var(tick); int cpu = smp_processor_id(); @@ -937,16 +946,22 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev, } /** - * intel_idle_s2idle - simplified "enter" callback routine for suspend-to-idle - * @dev: cpuidle_device - * @drv: cpuidle driver - * @index: state index + * intel_idle_s2idle - Ask the processor to enter the given idle state. + * @dev: cpuidle device of the target CPU. + * @drv: cpuidle driver (assumed to point to intel_idle_driver). + * @index: Target idle state index. + * + * Use the MWAIT instruction to notify the processor that the CPU represented by + * @dev is idle and it can try to enter the idle state corresponding to @index. + * + * Invoked as a suspend-to-idle callback routine with frozen user space, frozen + * scheduler tick and suspended scheduler clock on the target CPU. */ -static void intel_idle_s2idle(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) +static __cpuidle void intel_idle_s2idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { - unsigned long ecx = 1; /* break on interrupt flag */ unsigned long eax = flg2MWAIT(drv->states[index].flags); + unsigned long ecx = 1; /* break on interrupt flag */ mwait_idle_with_hints(eax, ecx); } From 30a996fbb359ed53536a055af84a54223beabf91 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:41:15 +0100 Subject: [PATCH 0122/2231] intel_idle: Relocate definitions of cpuidle callbacks Move the definitions of intel_idle() and intel_idle_s2idle() before the definitions of cpuidle_state structures referring to them to avoid having to use additional declarations of them (and drop those declarations). No functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 154 +++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 79 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 5adc058c705d..e0332d567735 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -83,10 +83,6 @@ struct idle_cpu { static const struct idle_cpu *icpu; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; -static int intel_idle(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index); -static void intel_idle_s2idle(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index); static struct cpuidle_state *cpuidle_state_table; /* @@ -112,6 +108,81 @@ static struct cpuidle_state *cpuidle_state_table; #define flg2MWAIT(flags) (((flags) >> 24) & 0xFF) #define MWAIT2flg(eax) ((eax & 0xFF) << 24) +/** + * intel_idle - Ask the processor to enter the given idle state. + * @dev: cpuidle device of the target CPU. + * @drv: cpuidle driver (assumed to point to intel_idle_driver). + * @index: Target idle state index. + * + * Use the MWAIT instruction to notify the processor that the CPU represented by + * @dev is idle and it can try to enter the idle state corresponding to @index. + * + * If the local APIC timer is not known to be reliable in the target idle state, + * enable one-shot tick broadcasting for the target CPU before executing MWAIT. + * + * Optionally call leave_mm() for the target CPU upfront to avoid wakeups due to + * flushing user TLBs. + * + * Must be called under local_irq_disable(). + */ +static __cpuidle int intel_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + struct cpuidle_state *state = &drv->states[index]; + unsigned long eax = flg2MWAIT(state->flags); + unsigned long ecx = 1; /* break on interrupt flag */ + bool uninitialized_var(tick); + int cpu = smp_processor_id(); + + /* + * leave_mm() to avoid costly and often unnecessary wakeups + * for flushing the user TLB's associated with the active mm. + */ + if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED) + leave_mm(cpu); + + if (!static_cpu_has(X86_FEATURE_ARAT) && !lapic_timer_always_reliable) { + /* + * Switch over to one-shot tick broadcast if the target C-state + * is deeper than C1. + */ + if ((eax >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) { + tick = true; + tick_broadcast_enter(); + } else { + tick = false; + } + } + + mwait_idle_with_hints(eax, ecx); + + if (!static_cpu_has(X86_FEATURE_ARAT) && tick) + tick_broadcast_exit(); + + return index; +} + +/** + * intel_idle_s2idle - Ask the processor to enter the given idle state. + * @dev: cpuidle device of the target CPU. + * @drv: cpuidle driver (assumed to point to intel_idle_driver). + * @index: Target idle state index. + * + * Use the MWAIT instruction to notify the processor that the CPU represented by + * @dev is idle and it can try to enter the idle state corresponding to @index. + * + * Invoked as a suspend-to-idle callback routine with frozen user space, frozen + * scheduler tick and suspended scheduler clock on the target CPU. + */ +static __cpuidle void intel_idle_s2idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + unsigned long eax = flg2MWAIT(drv->states[index].flags); + unsigned long ecx = 1; /* break on interrupt flag */ + + mwait_idle_with_hints(eax, ecx); +} + /* * States are indexed by the cstate number, * which is also the index into the MWAIT hint array. @@ -891,81 +962,6 @@ static struct cpuidle_state dnv_cstates[] = { .enter = NULL } }; -/** - * intel_idle - Ask the processor to enter the given idle state. - * @dev: cpuidle device of the target CPU. - * @drv: cpuidle driver (assumed to point to intel_idle_driver). - * @index: Target idle state index. - * - * Use the MWAIT instruction to notify the processor that the CPU represented by - * @dev is idle and it can try to enter the idle state corresponding to @index. - * - * If the local APIC timer is not known to be reliable in the target idle state, - * enable one-shot tick broadcasting for the target CPU before executing MWAIT. - * - * Optionally call leave_mm() for the target CPU upfront to avoid wakeups due to - * flushing user TLBs. - * - * Must be called under local_irq_disable(). - */ -static __cpuidle int intel_idle(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) -{ - struct cpuidle_state *state = &drv->states[index]; - unsigned long eax = flg2MWAIT(state->flags); - unsigned long ecx = 1; /* break on interrupt flag */ - bool uninitialized_var(tick); - int cpu = smp_processor_id(); - - /* - * leave_mm() to avoid costly and often unnecessary wakeups - * for flushing the user TLB's associated with the active mm. - */ - if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED) - leave_mm(cpu); - - if (!static_cpu_has(X86_FEATURE_ARAT) && !lapic_timer_always_reliable) { - /* - * Switch over to one-shot tick broadcast if the target C-state - * is deeper than C1. - */ - if ((eax >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) { - tick = true; - tick_broadcast_enter(); - } else { - tick = false; - } - } - - mwait_idle_with_hints(eax, ecx); - - if (!static_cpu_has(X86_FEATURE_ARAT) && tick) - tick_broadcast_exit(); - - return index; -} - -/** - * intel_idle_s2idle - Ask the processor to enter the given idle state. - * @dev: cpuidle device of the target CPU. - * @drv: cpuidle driver (assumed to point to intel_idle_driver). - * @index: Target idle state index. - * - * Use the MWAIT instruction to notify the processor that the CPU represented by - * @dev is idle and it can try to enter the idle state corresponding to @index. - * - * Invoked as a suspend-to-idle callback routine with frozen user space, frozen - * scheduler tick and suspended scheduler clock on the target CPU. - */ -static __cpuidle void intel_idle_s2idle(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) -{ - unsigned long eax = flg2MWAIT(drv->states[index].flags); - unsigned long ecx = 1; /* break on interrupt flag */ - - mwait_idle_with_hints(eax, ecx); -} - static const struct idle_cpu idle_cpu_nehalem = { .state_table = nehalem_cstates, .auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE, From 7f843dd71258ca0e01674be1008eca98917f3f9f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:41:24 +0100 Subject: [PATCH 0123/2231] intel_idle: Add __initdata annotations to init time variables Annotate static variables cpuidle_state_table and mwait_substates with __initdata, because they are only used during the initialization of the driver. Also notice that static variable icpu could be annotated analogously and the structure pointed to by it could be __initconst, but two of its fields are accessed via icpu in intel_idle_cpu_init() and auto_demotion_disable(), so introduce two new static variables, auto_demotion_disable_flags and disable_promotion_to_c1e, to hold the values of these fields, set them during the initialization and use them in those functions instead of accessing the source data structure via icpu. That allows icpu to be annotated with __initdata, so do that, and it will also allow some __initconst annotations to be added subsequently. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index e0332d567735..b7341e0b910a 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -65,7 +65,11 @@ static struct cpuidle_driver intel_idle_driver = { static int max_cstate = CPUIDLE_STATE_MAX - 1; static unsigned int disabled_states_mask; -static unsigned int mwait_substates; +static unsigned int mwait_substates __initdata; + +static unsigned long auto_demotion_disable_flags; +static bool disable_promotion_to_c1e; + static bool lapic_timer_always_reliable; struct idle_cpu { @@ -81,9 +85,9 @@ struct idle_cpu { bool use_acpi; }; -static const struct idle_cpu *icpu; +static const struct idle_cpu *icpu __initdata; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; -static struct cpuidle_state *cpuidle_state_table; +static struct cpuidle_state *cpuidle_state_table __initdata; /* * Enable this state by default even if the ACPI _CST does not list it. @@ -1519,7 +1523,7 @@ static void auto_demotion_disable(void) unsigned long long msr_bits; rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, msr_bits); - msr_bits &= ~(icpu->auto_demotion_disable_flags); + msr_bits &= ~auto_demotion_disable_flags; wrmsrl(MSR_PKG_CST_CONFIG_CONTROL, msr_bits); } @@ -1549,13 +1553,10 @@ static int intel_idle_cpu_init(unsigned int cpu) return -EIO; } - if (!icpu) - return 0; - - if (icpu->auto_demotion_disable_flags) + if (auto_demotion_disable_flags) auto_demotion_disable(); - if (icpu->disable_promotion_to_c1e) + if (disable_promotion_to_c1e) c1e_promotion_disable(); return 0; @@ -1633,6 +1634,8 @@ static int __init intel_idle_init(void) icpu = (const struct idle_cpu *)id->driver_data; if (icpu) { cpuidle_state_table = icpu->state_table; + auto_demotion_disable_flags = icpu->auto_demotion_disable_flags; + disable_promotion_to_c1e = icpu->disable_promotion_to_c1e; if (icpu->use_acpi || force_use_acpi) intel_idle_acpi_cst_extract(); } else if (!intel_idle_acpi_cst_extract()) { From ab1a8522d81ed79f66ba87a02046ef7523f0d702 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:45:06 +0100 Subject: [PATCH 0124/2231] intel_idle: Annotate init time data structures Add __initdata or __initconst annotations to the static data structures that are only used during the initialization of the driver. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 78 +++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index b7341e0b910a..5e627c40f6b0 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -192,7 +192,7 @@ static __cpuidle void intel_idle_s2idle(struct cpuidle_device *dev, * which is also the index into the MWAIT hint array. * Thus C0 is a dummy. */ -static struct cpuidle_state nehalem_cstates[] = { +static struct cpuidle_state nehalem_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -229,7 +229,7 @@ static struct cpuidle_state nehalem_cstates[] = { .enter = NULL } }; -static struct cpuidle_state snb_cstates[] = { +static struct cpuidle_state snb_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -274,7 +274,7 @@ static struct cpuidle_state snb_cstates[] = { .enter = NULL } }; -static struct cpuidle_state byt_cstates[] = { +static struct cpuidle_state byt_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -319,7 +319,7 @@ static struct cpuidle_state byt_cstates[] = { .enter = NULL } }; -static struct cpuidle_state cht_cstates[] = { +static struct cpuidle_state cht_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -364,7 +364,7 @@ static struct cpuidle_state cht_cstates[] = { .enter = NULL } }; -static struct cpuidle_state ivb_cstates[] = { +static struct cpuidle_state ivb_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -409,7 +409,7 @@ static struct cpuidle_state ivb_cstates[] = { .enter = NULL } }; -static struct cpuidle_state ivt_cstates[] = { +static struct cpuidle_state ivt_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -446,7 +446,7 @@ static struct cpuidle_state ivt_cstates[] = { .enter = NULL } }; -static struct cpuidle_state ivt_cstates_4s[] = { +static struct cpuidle_state ivt_cstates_4s[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -483,7 +483,7 @@ static struct cpuidle_state ivt_cstates_4s[] = { .enter = NULL } }; -static struct cpuidle_state ivt_cstates_8s[] = { +static struct cpuidle_state ivt_cstates_8s[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -520,7 +520,7 @@ static struct cpuidle_state ivt_cstates_8s[] = { .enter = NULL } }; -static struct cpuidle_state hsw_cstates[] = { +static struct cpuidle_state hsw_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -588,7 +588,7 @@ static struct cpuidle_state hsw_cstates[] = { { .enter = NULL } }; -static struct cpuidle_state bdw_cstates[] = { +static struct cpuidle_state bdw_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -657,7 +657,7 @@ static struct cpuidle_state bdw_cstates[] = { .enter = NULL } }; -static struct cpuidle_state skl_cstates[] = { +static struct cpuidle_state skl_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -726,7 +726,7 @@ static struct cpuidle_state skl_cstates[] = { .enter = NULL } }; -static struct cpuidle_state skx_cstates[] = { +static struct cpuidle_state skx_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -755,7 +755,7 @@ static struct cpuidle_state skx_cstates[] = { .enter = NULL } }; -static struct cpuidle_state atom_cstates[] = { +static struct cpuidle_state atom_cstates[] __initdata = { { .name = "C1E", .desc = "MWAIT 0x00", @@ -791,7 +791,7 @@ static struct cpuidle_state atom_cstates[] = { { .enter = NULL } }; -static struct cpuidle_state tangier_cstates[] = { +static struct cpuidle_state tangier_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -835,7 +835,7 @@ static struct cpuidle_state tangier_cstates[] = { { .enter = NULL } }; -static struct cpuidle_state avn_cstates[] = { +static struct cpuidle_state avn_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -855,7 +855,7 @@ static struct cpuidle_state avn_cstates[] = { { .enter = NULL } }; -static struct cpuidle_state knl_cstates[] = { +static struct cpuidle_state knl_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -876,7 +876,7 @@ static struct cpuidle_state knl_cstates[] = { .enter = NULL } }; -static struct cpuidle_state bxt_cstates[] = { +static struct cpuidle_state bxt_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -937,7 +937,7 @@ static struct cpuidle_state bxt_cstates[] = { .enter = NULL } }; -static struct cpuidle_state dnv_cstates[] = { +static struct cpuidle_state dnv_cstates[] __initdata = { { .name = "C1", .desc = "MWAIT 0x00", @@ -966,116 +966,116 @@ static struct cpuidle_state dnv_cstates[] = { .enter = NULL } }; -static const struct idle_cpu idle_cpu_nehalem = { +static const struct idle_cpu idle_cpu_nehalem __initconst = { .state_table = nehalem_cstates, .auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE, .disable_promotion_to_c1e = true, }; -static const struct idle_cpu idle_cpu_nhx = { +static const struct idle_cpu idle_cpu_nhx __initconst = { .state_table = nehalem_cstates, .auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE, .disable_promotion_to_c1e = true, .use_acpi = true, }; -static const struct idle_cpu idle_cpu_atom = { +static const struct idle_cpu idle_cpu_atom __initconst = { .state_table = atom_cstates, }; -static const struct idle_cpu idle_cpu_tangier = { +static const struct idle_cpu idle_cpu_tangier __initconst = { .state_table = tangier_cstates, }; -static const struct idle_cpu idle_cpu_lincroft = { +static const struct idle_cpu idle_cpu_lincroft __initconst = { .state_table = atom_cstates, .auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE, }; -static const struct idle_cpu idle_cpu_snb = { +static const struct idle_cpu idle_cpu_snb __initconst = { .state_table = snb_cstates, .disable_promotion_to_c1e = true, }; -static const struct idle_cpu idle_cpu_snx = { +static const struct idle_cpu idle_cpu_snx __initconst = { .state_table = snb_cstates, .disable_promotion_to_c1e = true, .use_acpi = true, }; -static const struct idle_cpu idle_cpu_byt = { +static const struct idle_cpu idle_cpu_byt __initconst = { .state_table = byt_cstates, .disable_promotion_to_c1e = true, .byt_auto_demotion_disable_flag = true, }; -static const struct idle_cpu idle_cpu_cht = { +static const struct idle_cpu idle_cpu_cht __initconst = { .state_table = cht_cstates, .disable_promotion_to_c1e = true, .byt_auto_demotion_disable_flag = true, }; -static const struct idle_cpu idle_cpu_ivb = { +static const struct idle_cpu idle_cpu_ivb __initconst = { .state_table = ivb_cstates, .disable_promotion_to_c1e = true, }; -static const struct idle_cpu idle_cpu_ivt = { +static const struct idle_cpu idle_cpu_ivt __initconst = { .state_table = ivt_cstates, .disable_promotion_to_c1e = true, .use_acpi = true, }; -static const struct idle_cpu idle_cpu_hsw = { +static const struct idle_cpu idle_cpu_hsw __initconst = { .state_table = hsw_cstates, .disable_promotion_to_c1e = true, }; -static const struct idle_cpu idle_cpu_hsx = { +static const struct idle_cpu idle_cpu_hsx __initconst = { .state_table = hsw_cstates, .disable_promotion_to_c1e = true, .use_acpi = true, }; -static const struct idle_cpu idle_cpu_bdw = { +static const struct idle_cpu idle_cpu_bdw __initconst = { .state_table = bdw_cstates, .disable_promotion_to_c1e = true, }; -static const struct idle_cpu idle_cpu_bdx = { +static const struct idle_cpu idle_cpu_bdx __initconst = { .state_table = bdw_cstates, .disable_promotion_to_c1e = true, .use_acpi = true, }; -static const struct idle_cpu idle_cpu_skl = { +static const struct idle_cpu idle_cpu_skl __initconst = { .state_table = skl_cstates, .disable_promotion_to_c1e = true, }; -static const struct idle_cpu idle_cpu_skx = { +static const struct idle_cpu idle_cpu_skx __initconst = { .state_table = skx_cstates, .disable_promotion_to_c1e = true, .use_acpi = true, }; -static const struct idle_cpu idle_cpu_avn = { +static const struct idle_cpu idle_cpu_avn __initconst = { .state_table = avn_cstates, .disable_promotion_to_c1e = true, .use_acpi = true, }; -static const struct idle_cpu idle_cpu_knl = { +static const struct idle_cpu idle_cpu_knl __initconst = { .state_table = knl_cstates, .use_acpi = true, }; -static const struct idle_cpu idle_cpu_bxt = { +static const struct idle_cpu idle_cpu_bxt __initconst = { .state_table = bxt_cstates, .disable_promotion_to_c1e = true, }; -static const struct idle_cpu idle_cpu_dnv = { +static const struct idle_cpu idle_cpu_dnv __initconst = { .state_table = dnv_cstates, .disable_promotion_to_c1e = true, .use_acpi = true, From 6eb0443ac89d8f256a89bb2a14c8238b86a92a61 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:45:18 +0100 Subject: [PATCH 0125/2231] intel_idle: Reorder declarations of static variables Reorder declarations of static variables so that the __initdata ones are declared together. No functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 5e627c40f6b0..3a93cd1036fb 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -65,7 +65,7 @@ static struct cpuidle_driver intel_idle_driver = { static int max_cstate = CPUIDLE_STATE_MAX - 1; static unsigned int disabled_states_mask; -static unsigned int mwait_substates __initdata; +static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static unsigned long auto_demotion_disable_flags; static bool disable_promotion_to_c1e; @@ -86,9 +86,10 @@ struct idle_cpu { }; static const struct idle_cpu *icpu __initdata; -static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static struct cpuidle_state *cpuidle_state_table __initdata; +static unsigned int mwait_substates __initdata; + /* * Enable this state by default even if the ACPI _CST does not list it. */ From 6eacb15fef4ef90b8c6467bd80b8be2de1fe1b3a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:45:29 +0100 Subject: [PATCH 0126/2231] intel_idle: Clean up kerneldoc comments for multiple functions Turn the description comments of some functions in the intel_idle driver into proper kerneldoc ones and clean them up. No functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 40 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 3a93cd1036fb..9575615c8f4a 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1288,11 +1288,11 @@ static inline void intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { } static inline bool intel_idle_off_by_default(u32 mwait_hint) { return false; } #endif /* !CONFIG_ACPI_PROCESSOR_CSTATE */ -/* - * ivt_idle_state_table_update(void) +/** + * ivt_idle_state_table_update - Tune the idle states table for Ivy Town. * - * Tune IVT multi-socket targets - * Assumption: num_sockets == (max_package_num + 1) + * Tune IVT multi-socket targets. + * Assumption: num_sockets == (max_package_num + 1). */ static void __init ivt_idle_state_table_update(void) { @@ -1338,11 +1338,11 @@ static unsigned long long __init irtl_2_usec(unsigned long long irtl) return div_u64((irtl & 0x3FF) * ns, NSEC_PER_USEC); } -/* - * bxt_idle_state_table_update(void) +/** + * bxt_idle_state_table_update - Fix up the Broxton idle states table. * - * On BXT, we trust the IRTL to show the definitive maximum latency - * We use the same value for target_residency. + * On BXT, trust the IRTL (Interrupt Response Time Limit) MSR to show the + * definitive maximum latency and use the same value for target_residency. */ static void __init bxt_idle_state_table_update(void) { @@ -1385,11 +1385,11 @@ static void __init bxt_idle_state_table_update(void) } } -/* - * sklh_idle_state_table_update(void) + +/** + * sklh_idle_state_table_update - Fix up the Sky Lake idle states table. * - * On SKL-H (model 0x5e) disable C8 and C9 if: - * C10 is enabled and SGX disabled + * On SKL-H (model 0x5e) skip C8 and C9 if C10 is enabled and SGX disabled. */ static void __init sklh_idle_state_table_update(void) { @@ -1500,9 +1500,9 @@ static void __init intel_idle_init_cstates_icpu(struct cpuidle_driver *drv) } } -/* - * intel_idle_cpuidle_driver_init() - * allocate, initialize cpuidle_states +/** + * intel_idle_cpuidle_driver_init - Create the list of available idle states. + * @drv: cpuidle driver structure to initialize. */ static void __init intel_idle_cpuidle_driver_init(struct cpuidle_driver *drv) { @@ -1537,10 +1537,12 @@ static void c1e_promotion_disable(void) wrmsrl(MSR_IA32_POWER_CTL, msr_bits); } -/* - * intel_idle_cpu_init() - * allocate, initialize, register cpuidle_devices - * @cpu: cpu/core to initialize +/** + * intel_idle_cpu_init - Register the target CPU with the cpuidle core. + * @cpu: CPU to initialize. + * + * Register a cpuidle device object for @cpu and update its MSRs in accordance + * with the processor model flags. */ static int intel_idle_cpu_init(unsigned int cpu) { From a472e4b5921e01c4ef84dd87d2bb30f1a6a896d4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:45:39 +0100 Subject: [PATCH 0127/2231] intel_idle: Define CPUIDLE_FLAG_TLB_FLUSHED as BIT(16) Use the BIT() macro for defining CPUIDLE_FLAG_TLB_FLUSHED instead of the hex bit encoding of the same value. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 9575615c8f4a..de1367d996c5 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -101,7 +101,7 @@ static unsigned int mwait_substates __initdata; * If this flag is set, SW flushes the TLB, so even if the * HW doesn't do the flushing, this flag is safe to use. */ -#define CPUIDLE_FLAG_TLB_FLUSHED 0x10000 +#define CPUIDLE_FLAG_TLB_FLUSHED BIT(16) /* * MWAIT takes an 8-bit "hint" in EAX "suggesting" From 317e5ec3ecaaa2d461d3044eb6cb9cc3a59de2f2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2020 18:45:49 +0100 Subject: [PATCH 0128/2231] intel_idle: Update copyright notice, known limitations and version Update the copyright notice in intel_idle.c to cover the recent changes, drop the description of a "known limitation" that is not a limitation any more and bump up the driver version number. No functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index de1367d996c5..65b84d5dc457 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -2,8 +2,9 @@ /* * intel_idle.c - native hardware idle loop for modern Intel processors * - * Copyright (c) 2013, Intel Corporation. + * Copyright (c) 2013 - 2020, Intel Corporation. * Len Brown + * Rafael J. Wysocki */ /* @@ -25,11 +26,6 @@ /* * Known limitations * - * The driver currently initializes for_each_online_cpu() upon modprobe. - * It it unaware of subsequent processors hot-added to the system. - * This means that if you boot with maxcpus=n and later online - * processors above n, those processors will use C1 only. - * * ACPI has a .suspend hack to turn off deep c-statees during suspend * to avoid complications with the lapic timer workaround. * Have not seen issues with suspend, but may need same workaround here. @@ -55,7 +51,7 @@ #include #include -#define INTEL_IDLE_VERSION "0.4.1" +#define INTEL_IDLE_VERSION "0.5.1" static struct cpuidle_driver intel_idle_driver = { .name = "intel_idle", From 36eb7dc1bd42fe5f850329c893768ff89b696fba Mon Sep 17 00:00:00 2001 From: Christoph Niedermaier Date: Tue, 11 Feb 2020 12:58:07 +0100 Subject: [PATCH 0129/2231] cpufreq: imx6q: Fixes unwanted cpu overclocking on i.MX6ULL imx6ul_opp_check_speed_grading is called for both i.MX6UL and i.MX6ULL. Since the i.MX6ULL was introduced to a separate ocotp compatible node later, it is possible that the i.MX6ULL has also dtbs with "fsl,imx6ull-ocotp". On a system without nvmem-cell speed grade a missing check on this node causes a driver fail without considering the cpu speed grade. This patch prevents unwanted cpu overclocking on i.MX6ULL with compatible node "fsl,imx6ull-ocotp" in old dtbs without nvmem-cell speed grade. Fixes: 2733fb0d0699 ("cpufreq: imx6q: read OCOTP through nvmem for imx6ul/imx6ull") Signed-off-by: Christoph Niedermaier Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx6q-cpufreq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 648a09a1778a..1fcbbd53a48a 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -280,6 +280,9 @@ static int imx6ul_opp_check_speed_grading(struct device *dev) void __iomem *base; np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-ocotp"); + if (!np) + np = of_find_compatible_node(NULL, NULL, + "fsl,imx6ull-ocotp"); if (!np) return -ENOENT; From 0ea4fb29c68950fbeea4e507e3c59ea35bb98ffd Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 11 Feb 2020 12:53:55 +0530 Subject: [PATCH 0130/2231] cpufreq: ti-cpufreq: Add support for OPP_PLUS DRA762 SoC introduces OPP_PLUS which runs at 1.8GHz. Add support for this OPP in ti-cpufreq driver. Acked-by: Dave Gerlach Signed-off-by: Lokesh Vutla Signed-off-by: Viresh Kumar --- drivers/cpufreq/ti-cpufreq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index 557cb513bf7f..ab0de27539ad 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -25,11 +25,14 @@ #define DRA7_EFUSE_HAS_OD_MPU_OPP 11 #define DRA7_EFUSE_HAS_HIGH_MPU_OPP 15 +#define DRA76_EFUSE_HAS_PLUS_MPU_OPP 18 #define DRA7_EFUSE_HAS_ALL_MPU_OPP 23 +#define DRA76_EFUSE_HAS_ALL_MPU_OPP 24 #define DRA7_EFUSE_NOM_MPU_OPP BIT(0) #define DRA7_EFUSE_OD_MPU_OPP BIT(1) #define DRA7_EFUSE_HIGH_MPU_OPP BIT(2) +#define DRA76_EFUSE_PLUS_MPU_OPP BIT(3) #define OMAP3_CONTROL_DEVICE_STATUS 0x4800244C #define OMAP3_CONTROL_IDCODE 0x4830A204 @@ -80,6 +83,10 @@ static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data, */ switch (efuse) { + case DRA76_EFUSE_HAS_PLUS_MPU_OPP: + case DRA76_EFUSE_HAS_ALL_MPU_OPP: + calculated_efuse |= DRA76_EFUSE_PLUS_MPU_OPP; + /* Fall through */ case DRA7_EFUSE_HAS_ALL_MPU_OPP: case DRA7_EFUSE_HAS_HIGH_MPU_OPP: calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP; From 34896620422eefe1bad998ed4b48117426ad81f9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Feb 2020 23:52:35 +0100 Subject: [PATCH 0131/2231] PM: QoS: Drop debugfs interface After commit c3082a674f46 ("PM: QoS: Get rid of unused flags") the only global PM QoS class in use is PM_QOS_CPU_DMA_LATENCY and the existing PM QoS debugfs interface has become overly complicated (as it takes other potentially possible PM QoS classes that are not there any more into account). It is also not particularly useful (the "type" of the PM_QOS_CPU_DMA_LATENCY is known, its aggregate value can be read from /dev/cpu_dma_latency and the number of requests in the queue does not really matter) and there are no known users depending on it. Moreover, there are dedicated trace events that can be used for tracking PM QoS usage with much higher precision. For these reasons, drop the PM QoS debugfs interface altogether. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson --- kernel/power/qos.c | 73 ++-------------------------------------------- 1 file changed, 2 insertions(+), 71 deletions(-) diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 83edf8698118..d932fa42e8e4 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -137,69 +137,6 @@ static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value) c->target_value = value; } -static int pm_qos_debug_show(struct seq_file *s, void *unused) -{ - struct pm_qos_object *qos = (struct pm_qos_object *)s->private; - struct pm_qos_constraints *c; - struct pm_qos_request *req; - char *type; - unsigned long flags; - int tot_reqs = 0; - int active_reqs = 0; - - if (IS_ERR_OR_NULL(qos)) { - pr_err("%s: bad qos param!\n", __func__); - return -EINVAL; - } - c = qos->constraints; - if (IS_ERR_OR_NULL(c)) { - pr_err("%s: Bad constraints on qos?\n", __func__); - return -EINVAL; - } - - /* Lock to ensure we have a snapshot */ - spin_lock_irqsave(&pm_qos_lock, flags); - if (plist_head_empty(&c->list)) { - seq_puts(s, "Empty!\n"); - goto out; - } - - switch (c->type) { - case PM_QOS_MIN: - type = "Minimum"; - break; - case PM_QOS_MAX: - type = "Maximum"; - break; - case PM_QOS_SUM: - type = "Sum"; - break; - default: - type = "Unknown"; - } - - plist_for_each_entry(req, &c->list, node) { - char *state = "Default"; - - if ((req->node).prio != c->default_value) { - active_reqs++; - state = "Active"; - } - tot_reqs++; - seq_printf(s, "%d: %d: %s\n", tot_reqs, - (req->node).prio, state); - } - - seq_printf(s, "Type=%s, Value=%d, Requests: active=%d / total=%d\n", - type, pm_qos_get_value(c), active_reqs, tot_reqs); - -out: - spin_unlock_irqrestore(&pm_qos_lock, flags); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(pm_qos_debug); - /** * pm_qos_update_target - manages the constraints list and calls the notifiers * if needed @@ -529,15 +466,12 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); /* User space interface to PM QoS classes via misc devices */ -static int register_pm_qos_misc(struct pm_qos_object *qos, struct dentry *d) +static int register_pm_qos_misc(struct pm_qos_object *qos) { qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; qos->pm_qos_power_miscdev.name = qos->name; qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; - debugfs_create_file(qos->name, S_IRUGO, d, (void *)qos, - &pm_qos_debug_fops); - return misc_register(&qos->pm_qos_power_miscdev); } @@ -631,14 +565,11 @@ static int __init pm_qos_power_init(void) { int ret = 0; int i; - struct dentry *d; BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES); - d = debugfs_create_dir("pm_qos", NULL); - for (i = PM_QOS_CPU_DMA_LATENCY; i < PM_QOS_NUM_CLASSES; i++) { - ret = register_pm_qos_misc(pm_qos_array[i], d); + ret = register_pm_qos_misc(pm_qos_array[i]); if (ret < 0) { pr_err("%s: %s setup failed\n", __func__, pm_qos_array[i]->name); From 882f7a4dae1db3f33934453d4194b829cd8ec5a5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 12 Feb 2020 11:18:53 +0100 Subject: [PATCH 0132/2231] usb: dwc3: qcom: Replace by The DWC3 USB driver is not a clock provider, and just needs to call of_clk_get_parent_count(). Hence it can include instead of . Signed-off-by: Geert Uytterhoeven Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200212101853.9349-1-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 261af9e38ddd..1dfd024cd06b 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include From b747038d9d14a2ca9e6f139a8584507d5aed8634 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Feb 2020 14:25:26 +0300 Subject: [PATCH 0133/2231] usb: typec: Make the attributes read-only when writing is not possible This affects the read-writable attribute files. Before this there was no way for the user to know is changing the value supported or not. >From now on those attribute files will be made read-only unless the underlying driver supports changing of the value. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200211112531.86510-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 65 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 7c44e930602f..a451ae181fe9 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -432,7 +432,28 @@ static struct attribute *typec_altmode_attrs[] = { &dev_attr_vdo.attr, NULL }; -ATTRIBUTE_GROUPS(typec_altmode); + +static umode_t typec_altmode_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj)); + + if (attr == &dev_attr_active.attr) + if (!adev->ops || !adev->ops->activate) + return 0444; + + return attr->mode; +} + +static struct attribute_group typec_altmode_group = { + .is_visible = typec_altmode_attr_is_visible, + .attrs = typec_altmode_attrs, +}; + +static const struct attribute_group *typec_altmode_groups[] = { + &typec_altmode_group, + NULL +}; static int altmode_id_get(struct device *dev) { @@ -1305,7 +1326,47 @@ static struct attribute *typec_attrs[] = { &dev_attr_port_type.attr, NULL, }; -ATTRIBUTE_GROUPS(typec); + +static umode_t typec_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct typec_port *port = to_typec_port(kobj_to_dev(kobj)); + + if (attr == &dev_attr_data_role.attr) { + if (port->cap->data != TYPEC_PORT_DRD || + !port->ops || !port->ops->dr_set) + return 0444; + } else if (attr == &dev_attr_power_role.attr) { + if (port->cap->type != TYPEC_PORT_DRP || + !port->cap->pd_revision || + !port->ops || !port->ops->pr_set) + return 0444; + } else if (attr == &dev_attr_vconn_source.attr) { + if (!port->cap->pd_revision || + !port->ops || !port->ops->vconn_set) + return 0444; + } else if (attr == &dev_attr_preferred_role.attr) { + if (port->cap->type != TYPEC_PORT_DRP || + !port->ops || !port->ops->try_role) + return 0444; + } else if (attr == &dev_attr_port_type.attr) { + if (port->cap->type != TYPEC_PORT_DRP || + !port->ops || !port->ops->port_type_set) + return 0444; + } + + return attr->mode; +} + +static struct attribute_group typec_group = { + .is_visible = typec_attr_is_visible, + .attrs = typec_attrs, +}; + +static const struct attribute_group *typec_groups[] = { + &typec_group, + NULL +}; static int typec_uevent(struct device *dev, struct kobj_uevent_env *env) { From 7932306a754a541f60b71b3551eb423f82d385cb Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Feb 2020 14:25:27 +0300 Subject: [PATCH 0134/2231] usb: typec: Hide the port_type attribute when it's not supported The port_type attribute is special. It is meant to allow changing the capability of the port in runtime. It is purely Linux kernel specific feature, i.e. the feature is not described in any of the USB specifications. Because of the special nature of this attribute, handling it differently compared to the other writable attributes, and hiding it when the underlying port interface (or just the driver) does not support the feature. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200211112531.86510-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index a451ae181fe9..7fed6855ad59 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1350,8 +1350,9 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, !port->ops || !port->ops->try_role) return 0444; } else if (attr == &dev_attr_port_type.attr) { - if (port->cap->type != TYPEC_PORT_DRP || - !port->ops || !port->ops->port_type_set) + if (!port->ops || !port->ops->port_type_set) + return 0; + if (port->cap->type != TYPEC_PORT_DRP) return 0444; } From ae4ba35d363816833ae3e72ecf5d2898c4996baf Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Feb 2020 14:25:28 +0300 Subject: [PATCH 0135/2231] usb: typec: Allow power role swapping even without USB PD Even though originally the USB Type-C Specification did not describe the steps for power role swapping without USB PD contract in place, it did not actually mean power role swap without USB PD was not allowed. The USB Type-C Specification did not clearly separate the data and power roles until in the release 1.2 which is why there also were no clear steps for the scenario where only the power role was swapped without USB PD contract before that. Since in the latest version of the specification the power role swap without USB PD is now clearly mentioned as allowed operation, removing the check that prevented power role swap without USB PD support. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200211112531.86510-4-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-typec | 14 +++++++------- drivers/usb/typec/class.c | 6 ------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index d7647b258c3c..0c2eb26fdc06 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -20,13 +20,13 @@ Date: April 2017 Contact: Heikki Krogerus Description: The supported power roles. This attribute can be used to request - power role swap on the port when the port supports USB Power - Delivery. Swapping is supported as synchronous operation, so - write(2) to the attribute will not return until the operation - has finished. The attribute is notified about role changes so - that poll(2) on the attribute wakes up. Change on the role will - also generate uevent KOBJ_CHANGE. The current role is show in - brackets, for example "[source] sink" when in source mode. + power role swap on the port. Swapping is supported as + synchronous operation, so write(2) to the attribute will not + return until the operation has finished. The attribute is + notified about role changes so that poll(2) on the attribute + wakes up. Change on the role will also generate uevent + KOBJ_CHANGE. The current role is show in brackets, for example + "[source] sink" when in source mode. Valid values: source, sink diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 7fed6855ad59..9cf4f6deb5a6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1112,11 +1112,6 @@ static ssize_t power_role_store(struct device *dev, struct typec_port *port = to_typec_port(dev); int ret; - if (!port->cap->pd_revision) { - dev_dbg(dev, "USB Power Delivery not supported\n"); - return -EOPNOTSUPP; - } - if (!port->ops || !port->ops->pr_set) { dev_dbg(dev, "power role swapping not supported\n"); return -EOPNOTSUPP; @@ -1338,7 +1333,6 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, return 0444; } else if (attr == &dev_attr_power_role.attr) { if (port->cap->type != TYPEC_PORT_DRP || - !port->cap->pd_revision || !port->ops || !port->ops->pr_set) return 0444; } else if (attr == &dev_attr_vconn_source.attr) { From 684cb4b79e594f786b942a3ac5097ecf0e9e3e08 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Feb 2020 14:25:29 +0300 Subject: [PATCH 0136/2231] usb: typec: Fix the description of struct typec_capability Removing descriptions of the mux and sw members. They are no longer part of the structure. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200211112531.86510-5-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index c358b3fd05c9..44d28387ced4 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -198,8 +198,6 @@ struct typec_operations { * @pd_revision: USB Power Delivery Specification revision if supported * @prefer_role: Initial role preference (DRP ports). * @accessory: Supported Accessory Modes - * @sw: Cable plug orientation switch - * @mux: Multiplexer switch for Alternate/Accessory Modes * @fwnode: Optional fwnode of the port * @driver_data: Private pointer for driver specific info * @ops: Port operations vector From bbe80c9a89b868e98ef0710cb03ee68dd78a4d8d Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Feb 2020 14:25:30 +0300 Subject: [PATCH 0137/2231] usb: typec: altmode: Remove the notification chain Using the generic notification chain is not reasonable with the alternate modes because it would require dependencies between the drivers of the components that need the notifications, and the typec drivers. There are no users for the alternate mode notifications, so removing the chain and the API for it completely. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200211112531.86510-6-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/usb/typec_bus.rst | 22 +------ drivers/usb/typec/bus.c | 12 +--- drivers/usb/typec/bus.h | 2 - drivers/usb/typec/class.c | 67 +--------------------- include/linux/usb/typec_altmode.h | 7 --- 5 files changed, 3 insertions(+), 107 deletions(-) diff --git a/Documentation/driver-api/usb/typec_bus.rst b/Documentation/driver-api/usb/typec_bus.rst index f47a69bff498..03dfa9c018b7 100644 --- a/Documentation/driver-api/usb/typec_bus.rst +++ b/Documentation/driver-api/usb/typec_bus.rst @@ -53,9 +53,7 @@ in need to reconfigure the pins on the connector, the alternate mode driver needs to notify the bus using :c:func:`typec_altmode_notify()`. The driver passes the negotiated SVID specific pin configuration value to the function as parameter. The bus driver will then configure the mux behind the connector using -that value as the state value for the mux, and also call blocking notification -chain to notify the external drivers about the state of the connector that need -to know it. +that value as the state value for the mux. NOTE: The SVID specific pin configuration values must always start from ``TYPEC_STATE_MODAL``. USB Type-C specification defines two default states for @@ -80,19 +78,6 @@ Helper macro ``TYPEC_MODAL_STATE()`` can also be used:: #define ALTMODEX_CONF_A = TYPEC_MODAL_STATE(0); #define ALTMODEX_CONF_B = TYPEC_MODAL_STATE(1); -Notification chain -~~~~~~~~~~~~~~~~~~ - -The drivers for the components that the alternate modes are designed for need to -get details regarding the results of the negotiation with the partner, and the -pin configuration of the connector. In case of DisplayPort alternate mode for -example, the GPU drivers will need to know those details. In case of -Thunderbolt alternate mode, the thunderbolt drivers will need to know them, and -so on. - -The notification chain is designed for this purpose. The drivers can register -notifiers with :c:func:`typec_altmode_register_notifier()`. - Cable plug alternate modes ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -129,8 +114,3 @@ Cable Plug operations .. kernel-doc:: drivers/usb/typec/bus.c :functions: typec_altmode_get_plug typec_altmode_put_plug - -Notifications -~~~~~~~~~~~~~ -.. kernel-doc:: drivers/usb/typec/class.c - :functions: typec_altmode_register_notifier typec_altmode_unregister_notifier diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 2e45eb479386..c823122f9cb7 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -30,17 +30,10 @@ static int typec_altmode_set_state(struct typec_altmode *adev, { bool is_port = is_typec_port(adev->dev.parent); struct altmode *port_altmode; - int ret; port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner; - ret = typec_altmode_set_mux(port_altmode, conf, data); - if (ret) - return ret; - - blocking_notifier_call_chain(&port_altmode->nh, conf, NULL); - - return 0; + return typec_altmode_set_mux(port_altmode, conf, data); } /* -------------------------------------------------------------------------- */ @@ -82,9 +75,6 @@ int typec_altmode_notify(struct typec_altmode *adev, if (ret) return ret; - blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh, - conf, data); - if (partner->adev.ops && partner->adev.ops->notify) return partner->adev.ops->notify(&partner->adev, conf, data); diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index 0c9661c96473..8ba8112d2740 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -22,8 +22,6 @@ struct altmode { struct altmode *partner; struct altmode *plug[2]; - - struct blocking_notifier_head nh; }; #define to_altmode(d) container_of(d, struct altmode, adev) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 9cf4f6deb5a6..12be5bb6d32c 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -206,69 +206,6 @@ static void typec_altmode_put_partner(struct altmode *altmode) put_device(&adev->dev); } -static void *typec_port_match(struct device_connection *con, int ep, void *data) -{ - struct device *dev; - - /* - * FIXME: Check does the fwnode supports the requested SVID. If it does - * we need to return ERR_PTR(-PROBE_DEFER) when there is no device. - */ - if (con->fwnode) - return class_find_device_by_fwnode(typec_class, con->fwnode); - - dev = class_find_device_by_name(typec_class, con->endpoint[ep]); - - return dev ? dev : ERR_PTR(-EPROBE_DEFER); -} - -struct typec_altmode * -typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, - struct notifier_block *nb) -{ - struct typec_device_id id = { svid, mode, }; - struct device *altmode_dev; - struct device *port_dev; - struct altmode *altmode; - int ret; - - /* Find the port linked to the caller */ - port_dev = device_connection_find_match(dev, NULL, NULL, - typec_port_match); - if (IS_ERR_OR_NULL(port_dev)) - return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV); - - /* Find the altmode with matching svid */ - altmode_dev = device_find_child(port_dev, &id, altmode_match); - - put_device(port_dev); - - if (!altmode_dev) - return ERR_PTR(-ENODEV); - - altmode = to_altmode(to_typec_altmode(altmode_dev)); - - /* Register notifier */ - ret = blocking_notifier_chain_register(&altmode->nh, nb); - if (ret) { - put_device(altmode_dev); - return ERR_PTR(ret); - } - - return &altmode->adev; -} -EXPORT_SYMBOL_GPL(typec_altmode_register_notifier); - -void typec_altmode_unregister_notifier(struct typec_altmode *adev, - struct notifier_block *nb) -{ - struct altmode *altmode = to_altmode(adev); - - blocking_notifier_chain_unregister(&altmode->nh, nb); - put_device(&adev->dev); -} -EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier); - /** * typec_altmode_update_active - Report Enter/Exit mode * @adev: Handle to the alternate mode @@ -538,9 +475,7 @@ typec_register_altmode(struct device *parent, dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id); /* Link partners and plugs with the ports */ - if (is_port) - BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh); - else + if (!is_port) typec_altmode_set_partner(alt); /* The partners are bind to drivers */ diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 923ff3af0628..d834e236c6df 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -126,13 +126,6 @@ void typec_altmode_put_plug(struct typec_altmode *plug); struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, size_t n, u16 svid, u8 mode); -struct typec_altmode * -typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, - struct notifier_block *nb); - -void typec_altmode_unregister_notifier(struct typec_altmode *adev, - struct notifier_block *nb); - /** * typec_altmode_get_orientation - Get cable plug orientation * altmode: Handle to the alternate mode From d80bdabea98698f14f741600595b15db051c2bae Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Feb 2020 14:25:31 +0300 Subject: [PATCH 0138/2231] usb: typec: mux: Drop support for device name matching There are no more users for the old device connection descriptions that used device names. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200211112531.86510-7-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 5baf0f416c73..b952fa2fff39 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -17,11 +17,6 @@ #include "bus.h" -static int name_match(struct device *dev, const void *name) -{ - return !strcmp((const char *)name, dev_name(dev)); -} - static bool dev_name_ends_with(struct device *dev, const char *suffix) { const char *name = dev_name(dev); @@ -44,16 +39,11 @@ static void *typec_switch_match(struct device_connection *con, int ep, { struct device *dev; - if (con->fwnode) { - if (con->id && !fwnode_property_present(con->fwnode, con->id)) - return NULL; + if (con->id && !fwnode_property_present(con->fwnode, con->id)) + return NULL; - dev = class_find_device(&typec_mux_class, NULL, con->fwnode, - switch_fwnode_match); - } else { - dev = class_find_device(&typec_mux_class, NULL, - con->endpoint[ep], name_match); - } + dev = class_find_device(&typec_mux_class, NULL, con->fwnode, + switch_fwnode_match); return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -191,13 +181,6 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data) u16 *val; int i; - if (!con->fwnode) { - dev = class_find_device(&typec_mux_class, NULL, - con->endpoint[ep], name_match); - - return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER); - } - /* * Check has the identifier already been "consumed". If it * has, no need to do any extra connection identification. From 6a9746fb0b1bf79777b709dd9f045ded89c6b373 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 17:21:48 -0600 Subject: [PATCH 0139/2231] USB: atm: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200211232148.GA20644@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 635cf0466b59..e9fed9a88737 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -350,7 +350,7 @@ struct l1_code { u8 string_header[E4_L1_STRING_HEADER]; u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER]; struct block_index page_header[E4_NO_SWAPPAGE_HEADERS]; - u8 code[0]; + u8 code[]; } __packed; /* structures describing a block within a DSP page */ From ef0f7d1877ac21fd926f2349e6a025c201651cf7 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:28 +0200 Subject: [PATCH 0140/2231] USB: Export generic USB device driver functions This will make it possible to implement device drivers which extend the generic driver without needing to reimplement it. Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-2-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 16 ++++++++-------- drivers/usb/core/usb.h | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 38f8b3e31762..28ece4d77749 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -195,7 +195,7 @@ int usb_choose_configuration(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_choose_configuration); -static int generic_probe(struct usb_device *udev) +int usb_generic_driver_probe(struct usb_device *udev) { int err, c; @@ -222,7 +222,7 @@ static int generic_probe(struct usb_device *udev) return 0; } -static void generic_disconnect(struct usb_device *udev) +void usb_generic_driver_disconnect(struct usb_device *udev) { usb_notify_remove_device(udev); @@ -234,7 +234,7 @@ static void generic_disconnect(struct usb_device *udev) #ifdef CONFIG_PM -static int generic_suspend(struct usb_device *udev, pm_message_t msg) +int usb_generic_driver_suspend(struct usb_device *udev, pm_message_t msg) { int rc; @@ -262,7 +262,7 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) return rc; } -static int generic_resume(struct usb_device *udev, pm_message_t msg) +int usb_generic_driver_resume(struct usb_device *udev, pm_message_t msg) { int rc; @@ -285,11 +285,11 @@ static int generic_resume(struct usb_device *udev, pm_message_t msg) struct usb_device_driver usb_generic_driver = { .name = "usb", - .probe = generic_probe, - .disconnect = generic_disconnect, + .probe = usb_generic_driver_probe, + .disconnect = usb_generic_driver_disconnect, #ifdef CONFIG_PM - .suspend = generic_suspend, - .resume = generic_resume, + .suspend = usb_generic_driver_suspend, + .resume = usb_generic_driver_resume, #endif .supports_autosuspend = 1, }; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index cf4783cf661a..bbe24817315e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -47,6 +47,12 @@ extern void usb_release_bos_descriptor(struct usb_device *dev); extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_choose_configuration(struct usb_device *udev); +extern int usb_generic_driver_probe(struct usb_device *udev); +extern void usb_generic_driver_disconnect(struct usb_device *udev); +extern int usb_generic_driver_suspend(struct usb_device *udev, + pm_message_t msg); +extern int usb_generic_driver_resume(struct usb_device *udev, + pm_message_t msg); static inline unsigned usb_get_max_power(struct usb_device *udev, struct usb_host_config *c) From c9d503370f240934f3c1c5da4c6c2452a7d05db2 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:29 +0200 Subject: [PATCH 0141/2231] USB: Make it possible to "subclass" usb_device_driver The kernel currenly has only 2 usb_device_drivers, one generic one, one that completely replaces the generic one to make USB devices usable over a network. Use the newly exported generic driver functions when a driver declares to want them run, in addition to its own code. This makes it possible to write drivers that extend the generic USB driver. Note that this patch is not enough for another driver to automatically get selected. Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-3-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 26 +++++++++++++++++++++----- include/linux/usb.h | 4 ++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2b27d232d7a7..d3787d084937 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -261,9 +261,16 @@ static int usb_probe_device(struct device *dev) */ if (!udriver->supports_autosuspend) error = usb_autoresume_device(udev); + if (error) + return error; - if (!error) - error = udriver->probe(udev); + if (udriver->generic_subclass) + error = usb_generic_driver_probe(udev); + if (error) + return error; + + error = udriver->probe(udev); + /* TODO: fallback to generic driver in case of error */ return error; } @@ -273,7 +280,10 @@ static int usb_unbind_device(struct device *dev) struct usb_device *udev = to_usb_device(dev); struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); - udriver->disconnect(udev); + if (udriver->disconnect) + udriver->disconnect(udev); + if (udriver->generic_subclass) + usb_generic_driver_disconnect(udev); if (!udriver->supports_autosuspend) usb_autosuspend_device(udev); return 0; @@ -1149,7 +1159,10 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) udev->do_remote_wakeup = 0; udriver = &usb_generic_driver; } - status = udriver->suspend(udev, msg); + if (udriver->suspend) + status = udriver->suspend(udev, msg); + if (status == 0 && udriver->generic_subclass) + status = usb_generic_driver_suspend(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); @@ -1181,7 +1194,10 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) udev->reset_resume = 1; udriver = to_usb_device_driver(udev->dev.driver); - status = udriver->resume(udev, msg); + if (udriver->generic_subclass) + status = usb_generic_driver_resume(udev, msg); + if (status == 0 && udriver->resume) + status = udriver->resume(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); diff --git a/include/linux/usb.h b/include/linux/usb.h index e656e7b4b1e4..94bd3b48a485 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1228,6 +1228,9 @@ struct usb_driver { * @drvwrap: Driver-model core structure wrapper. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend * for devices bound to this driver. + * @generic_subclass: if set to 1, the generic USB driver's probe, disconnect, + * resume and suspend functions will be called in addition to the driver's + * own, so this part of the setup does not need to be replicated. * * USB drivers must provide all the fields listed above except drvwrap. */ @@ -1242,6 +1245,7 @@ struct usb_device_driver { const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; unsigned int supports_autosuspend:1; + unsigned int generic_subclass:1; }; #define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \ drvwrap.driver) From aeebf2b5466506546c47ca68477d4aa8a96377a6 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:30 +0200 Subject: [PATCH 0142/2231] USB: Implement usb_device_match_id() Match a usb_device with a table of IDs. Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-4-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 14 ++++++++++++++ drivers/usb/core/usb.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d3787d084937..697898327b44 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -800,6 +800,20 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface, } EXPORT_SYMBOL_GPL(usb_match_id); +const struct usb_device_id *usb_device_match_id(struct usb_device *udev, + const struct usb_device_id *id) +{ + if (!id) + return NULL; + + for (; id->idVendor || id->idProduct ; id++) { + if (usb_match_device(udev, id)) + return id; + } + + return NULL; +} + static int usb_device_match(struct device *dev, struct device_driver *drv) { /* devices and interfaces are handled separately */ diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index bbe24817315e..f1dc63848219 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -69,6 +69,8 @@ extern int usb_match_one_id_intf(struct usb_device *dev, const struct usb_device_id *id); extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); +extern const struct usb_device_id *usb_device_match_id(struct usb_device *udev, + const struct usb_device_id *id); extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev); From 88b7381a939de0fa1f1b1629c56b03dca7077309 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:31 +0200 Subject: [PATCH 0143/2231] USB: Select better matching USB drivers when available Now that USB device drivers can reuse code from the generic USB device driver, we need to make sure that they get selected rather than the generic driver. Add an id_table and match vfunc to the usb_device_driver struct, which will get used to select a better matching driver at ->probe time. This is a similar mechanism to that used in the HID drivers, with the generic driver being selected unless there's a better matching one found in the registered drivers (see hid_generic_match() in drivers/hid/hid-generic.c). Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-5-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 15 +++++++++++++-- drivers/usb/core/generic.c | 29 +++++++++++++++++++++++++++++ include/linux/usb.h | 2 ++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 697898327b44..9d1502a9571d 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -818,13 +818,24 @@ static int usb_device_match(struct device *dev, struct device_driver *drv) { /* devices and interfaces are handled separately */ if (is_usb_device(dev)) { + struct usb_device *udev; + struct usb_device_driver *udrv; /* interface drivers never match devices */ if (!is_usb_device_driver(drv)) return 0; - /* TODO: Add real matching code */ - return 1; + udev = to_usb_device(dev); + udrv = to_usb_device_driver(drv); + + if (udrv->id_table && + usb_device_match_id(udev, udrv->id_table) != NULL) { + return 1; + } + + if (udrv->match) + return udrv->match(udev); + return 0; } else if (is_usb_interface(dev)) { struct usb_interface *intf; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 28ece4d77749..84da85c13825 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -195,6 +195,34 @@ int usb_choose_configuration(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_choose_configuration); +static int __check_usb_generic(struct device_driver *drv, void *data) +{ + struct usb_device *udev = data; + struct usb_device_driver *udrv; + + if (!is_usb_device_driver(drv)) + return 0; + udrv = to_usb_device_driver(drv); + if (udrv == &usb_generic_driver) + return 0; + if (!udrv->id_table) + return 0; + + return usb_device_match_id(udev, udrv->id_table) != NULL; +} + +static bool usb_generic_driver_match(struct usb_device *udev) +{ + /* + * If any other driver wants the device, leave the device to this other + * driver. + */ + if (bus_for_each_drv(&usb_bus_type, NULL, udev, __check_usb_generic)) + return false; + + return true; +} + int usb_generic_driver_probe(struct usb_device *udev) { int err, c; @@ -285,6 +313,7 @@ int usb_generic_driver_resume(struct usb_device *udev, pm_message_t msg) struct usb_device_driver usb_generic_driver = { .name = "usb", + .match = usb_generic_driver_match, .probe = usb_generic_driver_probe, .disconnect = usb_generic_driver_disconnect, #ifdef CONFIG_PM diff --git a/include/linux/usb.h b/include/linux/usb.h index 94bd3b48a485..3663bd7b1fa4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1237,6 +1237,7 @@ struct usb_driver { struct usb_device_driver { const char *name; + bool (*match) (struct usb_device *udev); int (*probe) (struct usb_device *udev); void (*disconnect) (struct usb_device *udev); @@ -1244,6 +1245,7 @@ struct usb_device_driver { int (*resume) (struct usb_device *udev, pm_message_t message); const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; + const struct usb_device_id *id_table; unsigned int supports_autosuspend:1; unsigned int generic_subclass:1; }; From 77419aa403ca1395f66e1e3de87743f54ba144b6 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:32 +0200 Subject: [PATCH 0144/2231] USB: Fallback to generic driver when specific driver fails If ->probe fails for a device specific driver, ask the driver core to reprobe us, after having flagged the device for the generic driver to be forced. Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-6-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 5 ++++- drivers/usb/core/generic.c | 3 +++ include/linux/usb.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 9d1502a9571d..f81606c6a35b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -270,7 +270,10 @@ static int usb_probe_device(struct device *dev) return error; error = udriver->probe(udev); - /* TODO: fallback to generic driver in case of error */ + if (error == -ENODEV && udriver != &usb_generic_driver) { + udev->use_generic_driver = 1; + return -EPROBE_DEFER; + } return error; } diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 84da85c13825..4626227a6dd2 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -213,6 +213,9 @@ static int __check_usb_generic(struct device_driver *drv, void *data) static bool usb_generic_driver_match(struct usb_device *udev) { + if (udev->use_generic_driver) + return true; + /* * If any other driver wants the device, leave the device to this other * driver. diff --git a/include/linux/usb.h b/include/linux/usb.h index 3663bd7b1fa4..ca1a5f1e1c5e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -708,6 +708,7 @@ struct usb_device { unsigned lpm_disable_count; u16 hub_delay; + unsigned use_generic_driver:1; }; #define to_usb_device(d) container_of(d, struct usb_device, dev) From 249fa8217b846a7c031b997bd4ea70d65d3ff774 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:33 +0200 Subject: [PATCH 0145/2231] USB: Add driver to control USB fast charge for iOS devices iOS devices will not draw more than 500mA unless instructed to do so. Setting the charge type power supply property to "fast" tells the device to start drawing more power, using the same procedure that official "MFi" chargers would. Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-7-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 + drivers/usb/misc/Kconfig | 10 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/apple-mfi-fastcharge.c | 237 ++++++++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 drivers/usb/misc/apple-mfi-fastcharge.c diff --git a/MAINTAINERS b/MAINTAINERS index 38fe2f3f7b6f..62d39710c3f3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17182,6 +17182,12 @@ S: Maintained F: Documentation/usb/acm.rst F: drivers/usb/class/cdc-acm.* +USB APPLE MFI FASTCHARGE DRIVER +M: Bastien Nocera +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/usb/misc/apple-mfi-fastcharge.c + USB AR5523 WIRELESS DRIVER M: Pontus Fuchs L: linux-wireless@vger.kernel.org diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 834b2494da73..833a460ee55a 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -137,6 +137,16 @@ config USB_APPLEDISPLAY Say Y here if you want to control the backlight of Apple Cinema Displays over USB. This driver provides a sysfs interface. +config APPLE_MFI_FASTCHARGE + tristate "Fast charge control for iOS devices" + select POWER_SUPPLY + help + Say Y here if you want to control whether iOS devices will + fast charge from the USB interface, as implemented in "MFi" + chargers. + + It is safe to say M here. + source "drivers/usb/misc/sisusbvga/Kconfig" config USB_LD diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 0d416eb624bb..da39bddb0604 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_USB_EMI26) += emi26.o obj-$(CONFIG_USB_EMI62) += emi62.o obj-$(CONFIG_USB_EZUSB_FX2) += ezusb.o obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o +obj-$(CONFIG_APPLE_MFI_FASTCHARGE) += apple-mfi-fastcharge.o obj-$(CONFIG_USB_IDMOUSE) += idmouse.o obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c new file mode 100644 index 000000000000..f1c4461a9a3c --- /dev/null +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Fast-charge control for Apple "MFi" devices + * + * Copyright (C) 2019 Bastien Nocera + */ + +/* Standard include files */ +#include +#include +#include +#include + +MODULE_AUTHOR("Bastien Nocera "); +MODULE_DESCRIPTION("Fast-charge control for Apple \"MFi\" devices"); +MODULE_LICENSE("GPL"); + +#define TRICKLE_CURRENT_MA 0 +#define FAST_CURRENT_MA 2500 + +#define APPLE_VENDOR_ID 0x05ac /* Apple */ + +/* The product ID is defined as starting with 0x12nn, as per the + * "Choosing an Apple Device USB Configuration" section in + * release R9 (2012) of the "MFi Accessory Hardware Specification" + * + * To distinguish an Apple device, a USB host can check the device + * descriptor of attached USB devices for the following fields: + * ■ Vendor ID: 0x05AC + * ■ Product ID: 0x12nn + * + * Those checks will be done in .match() and .probe(). + */ + +static const struct usb_device_id mfi_fc_id_table[] = { + { .idVendor = APPLE_VENDOR_ID, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR }, + {}, +}; + +MODULE_DEVICE_TABLE(usb, mfi_fc_id_table); + +/* Driver-local specific stuff */ +struct mfi_device { + struct usb_device *udev; + struct power_supply *battery; + int charge_type; +}; + +static int apple_mfi_fc_set_charge_type(struct mfi_device *mfi, + const union power_supply_propval *val) +{ + int current_ma; + int retval; + __u8 request_type; + + if (mfi->charge_type == val->intval) { + dev_dbg(&mfi->udev->dev, "charge type %d already set\n", + mfi->charge_type); + return 0; + } + + switch (val->intval) { + case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: + current_ma = TRICKLE_CURRENT_MA; + break; + case POWER_SUPPLY_CHARGE_TYPE_FAST: + current_ma = FAST_CURRENT_MA; + break; + default: + return -EINVAL; + } + + request_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + retval = usb_control_msg(mfi->udev, usb_sndctrlpipe(mfi->udev, 0), + 0x40, /* Vendor‐defined power request */ + request_type, + current_ma, /* wValue, current offset */ + current_ma, /* wIndex, current offset */ + NULL, 0, USB_CTRL_GET_TIMEOUT); + if (retval) { + dev_dbg(&mfi->udev->dev, "retval = %d\n", retval); + return retval; + } + + mfi->charge_type = val->intval; + + return 0; +} + +static int apple_mfi_fc_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mfi_device *mfi = power_supply_get_drvdata(psy); + + dev_dbg(&mfi->udev->dev, "prop: %d\n", psp); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = mfi->charge_type; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + default: + return -ENODATA; + } + + return 0; +} + +static int apple_mfi_fc_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct mfi_device *mfi = power_supply_get_drvdata(psy); + int ret; + + dev_dbg(&mfi->udev->dev, "prop: %d\n", psp); + + ret = pm_runtime_get_sync(&mfi->udev->dev); + if (ret < 0) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = apple_mfi_fc_set_charge_type(mfi, val); + break; + default: + ret = -EINVAL; + } + + pm_runtime_mark_last_busy(&mfi->udev->dev); + pm_runtime_put_autosuspend(&mfi->udev->dev); + + return ret; +} + +static int apple_mfi_fc_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + return 1; + default: + return 0; + } +} + +static enum power_supply_property apple_mfi_fc_properties[] = { + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_SCOPE +}; + +static const struct power_supply_desc apple_mfi_fc_desc = { + .name = "apple_mfi_fastcharge", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = apple_mfi_fc_properties, + .num_properties = ARRAY_SIZE(apple_mfi_fc_properties), + .get_property = apple_mfi_fc_get_property, + .set_property = apple_mfi_fc_set_property, + .property_is_writeable = apple_mfi_fc_property_is_writeable +}; + +static int mfi_fc_probe(struct usb_device *udev) +{ + struct power_supply_config battery_cfg = {}; + struct mfi_device *mfi = NULL; + int err; + + /* See comment above mfi_fc_id_table[] */ + if (udev->descriptor.idProduct < 0x1200 || + udev->descriptor.idProduct > 0x12ff) { + return -ENODEV; + } + + mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); + if (!mfi) { + err = -ENOMEM; + goto error; + } + + battery_cfg.drv_data = mfi; + + mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + mfi->battery = power_supply_register(&udev->dev, + &apple_mfi_fc_desc, + &battery_cfg); + if (IS_ERR(mfi->battery)) { + dev_err(&udev->dev, "Can't register battery\n"); + err = PTR_ERR(mfi->battery); + goto error; + } + + mfi->udev = usb_get_dev(udev); + dev_set_drvdata(&udev->dev, mfi); + + return 0; + +error: + kfree(mfi); + return err; +} + +static void mfi_fc_disconnect(struct usb_device *udev) +{ + struct mfi_device *mfi; + + mfi = dev_get_drvdata(&udev->dev); + if (mfi->battery) + power_supply_unregister(mfi->battery); + dev_set_drvdata(&udev->dev, NULL); + usb_put_dev(mfi->udev); + kfree(mfi); +} + +static struct usb_device_driver mfi_fc_driver = { + .name = "apple-mfi-fastcharge", + .probe = mfi_fc_probe, + .disconnect = mfi_fc_disconnect, + .id_table = mfi_fc_id_table, + .generic_subclass = 1, +}; + +static int __init mfi_fc_driver_init(void) +{ + return usb_register_device_driver(&mfi_fc_driver, THIS_MODULE); +} + +static void __exit mfi_fc_driver_exit(void) +{ + usb_deregister_device_driver(&mfi_fc_driver); +} + +module_init(mfi_fc_driver_init); +module_exit(mfi_fc_driver_exit); From 147eda2974d47b51fc2ae01f2fec9ea3b3845697 Mon Sep 17 00:00:00 2001 From: Marcelo Diop-Gonzalez Date: Wed, 12 Feb 2020 13:43:29 -0500 Subject: [PATCH 0146/2231] staging: vc04_services: remove unused function vchiq_get_service_fourcc() doesn't seem to be used anywhere Signed-off-by: Marcelo Diop-Gonzalez Link: https://lore.kernel.org/r/ed8b0034e316b2a81b621e9fca43f8368334b191.1581532523.git.marcgonzalez@google.com Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_core.c | 8 -------- .../staging/vc04_services/interface/vchiq_arm/vchiq_if.h | 2 -- 2 files changed, 10 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index d5957411d906..4f8b59deaec9 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -315,14 +315,6 @@ vchiq_get_service_userdata(unsigned int handle) return service ? service->base.userdata : NULL; } -int -vchiq_get_service_fourcc(unsigned int handle) -{ - struct vchiq_service *service = handle_to_service(handle); - - return service ? service->base.fourcc : 0; -} - static void mark_service_closing_internal(struct vchiq_service *service, int sh_thread) { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h index 07c6a3db5ab6..39b77ea19210 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h @@ -13,7 +13,6 @@ #define VCHIQ_MAKE_FOURCC(x0, x1, x2, x3) \ (((x0) << 24) | ((x1) << 16) | ((x2) << 8) | (x3)) #define VCHIQ_GET_SERVICE_USERDATA(service) vchiq_get_service_userdata(service) -#define VCHIQ_GET_SERVICE_FOURCC(service) vchiq_get_service_fourcc(service) enum vchiq_reason { VCHIQ_SERVICE_OPENED, /* service, -, - */ @@ -128,7 +127,6 @@ extern enum vchiq_status vchiq_bulk_receive_handle(unsigned int service, enum vchiq_bulk_mode mode); extern int vchiq_get_client_id(unsigned int service); extern void *vchiq_get_service_userdata(unsigned int service); -extern int vchiq_get_service_fourcc(unsigned int service); extern void vchiq_get_config(struct vchiq_config *config); extern enum vchiq_status vchiq_set_service_option(unsigned int service, enum vchiq_service_option option, int value); From 110700173e975bb271539d2cdb24f17a1420f037 Mon Sep 17 00:00:00 2001 From: Marcelo Diop-Gonzalez Date: Wed, 12 Feb 2020 13:43:30 -0500 Subject: [PATCH 0147/2231] staging: vc04_services: remove unneeded parentheses there are extra parentheses around many conditional statements that make things a little harder to read Signed-off-by: Marcelo Diop-Gonzalez Link: https://lore.kernel.org/r/41511abf64f73af62f21f8e0c7457edc289af905.1581532523.git.marcgonzalez@google.com Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_core.c | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 4f8b59deaec9..72bfa0f73958 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -138,8 +138,8 @@ find_service_by_handle(unsigned int handle) spin_lock(&service_spinlock); service = handle_to_service(handle); - if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && - (service->handle == handle)) { + if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && + service->handle == handle) { WARN_ON(service->ref_count == 0); service->ref_count++; } else @@ -161,7 +161,7 @@ find_service_by_port(struct vchiq_state *state, int localport) if ((unsigned int)localport <= VCHIQ_PORT_MAX) { spin_lock(&service_spinlock); service = state->services[localport]; - if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE)) { + if (service && service->srvstate != VCHIQ_SRVSTATE_FREE) { WARN_ON(service->ref_count == 0); service->ref_count++; } else @@ -184,9 +184,9 @@ find_service_for_instance(struct vchiq_instance *instance, spin_lock(&service_spinlock); service = handle_to_service(handle); - if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && - (service->handle == handle) && - (service->instance == instance)) { + if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && + service->handle == handle && + service->instance == instance) { WARN_ON(service->ref_count == 0); service->ref_count++; } else @@ -209,10 +209,10 @@ find_closed_service_for_instance(struct vchiq_instance *instance, spin_lock(&service_spinlock); service = handle_to_service(handle); if (service && - ((service->srvstate == VCHIQ_SRVSTATE_FREE) || - (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) && - (service->handle == handle) && - (service->instance == instance)) { + (service->srvstate == VCHIQ_SRVSTATE_FREE || + service->srvstate == VCHIQ_SRVSTATE_CLOSED) && + service->handle == handle && + service->instance == instance) { WARN_ON(service->ref_count == 0); service->ref_count++; } else @@ -237,8 +237,8 @@ next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *insta while (idx < state->unused_service) { struct vchiq_service *srv = state->services[idx++]; - if (srv && (srv->srvstate != VCHIQ_SRVSTATE_FREE) && - (srv->instance == instance)) { + if (srv && srv->srvstate != VCHIQ_SRVSTATE_FREE && + srv->instance == instance) { service = srv; WARN_ON(service->ref_count == 0); service->ref_count++; @@ -464,10 +464,10 @@ get_listening_service(struct vchiq_state *state, int fourcc) struct vchiq_service *service = state->services[i]; if (service && - (service->public_fourcc == fourcc) && - ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) || - ((service->srvstate == VCHIQ_SRVSTATE_OPEN) && - (service->remoteport == VCHIQ_PORT_FREE)))) { + service->public_fourcc == fourcc && + (service->srvstate == VCHIQ_SRVSTATE_LISTENING || + (service->srvstate == VCHIQ_SRVSTATE_OPEN && + service->remoteport == VCHIQ_PORT_FREE))) { lock_service(service); return service; } @@ -485,8 +485,8 @@ get_connected_service(struct vchiq_state *state, unsigned int port) for (i = 0; i < state->unused_service; i++) { struct vchiq_service *service = state->services[i]; - if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN) - && (service->remoteport == port)) { + if (service && service->srvstate == VCHIQ_SRVSTATE_OPEN && + service->remoteport == port) { lock_service(service); return service; } From 032974659b36827e49d6d872dd42f9bab788a6d6 Mon Sep 17 00:00:00 2001 From: Marcelo Diop-Gonzalez Date: Wed, 12 Feb 2020 13:43:31 -0500 Subject: [PATCH 0148/2231] staging: vc04_services: fix indentation alignment in a few places This fixes some checkpatch warnings about incorrect indentation levels Signed-off-by: Marcelo Diop-Gonzalez Link: https://lore.kernel.org/r/feadcde28a987fad12011a5f17b29f2147c09e12.1581532523.git.marcgonzalez@google.com Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 72bfa0f73958..b2d9013b7f79 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -2413,13 +2413,13 @@ vchiq_open_service_internal(struct vchiq_service *service, int client_id) status = VCHIQ_RETRY; vchiq_release_service_internal(service); } else if ((service->srvstate != VCHIQ_SRVSTATE_OPEN) && - (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { + (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) vchiq_log_error(vchiq_core_log_level, - "%d: osi - srvstate = %s (ref %d)", - service->state->id, - srvstate_names[service->srvstate], - service->ref_count); + "%d: osi - srvstate = %s (ref %d)", + service->state->id, + srvstate_names[service->srvstate], + service->ref_count); status = VCHIQ_ERROR; VCHIQ_SERVICE_STATS_INC(service, error_count); vchiq_release_service_internal(service); @@ -3427,8 +3427,8 @@ int vchiq_dump_service_state(void *dump_context, struct vchiq_service *service) int err; len = scnprintf(buf, sizeof(buf), "Service %u: %s (ref %u)", - service->localport, srvstate_names[service->srvstate], - service->ref_count - 1); /*Don't include the lock just taken*/ + service->localport, srvstate_names[service->srvstate], + service->ref_count - 1); /*Don't include the lock just taken*/ if (service->srvstate != VCHIQ_SRVSTATE_FREE) { char remoteport[30]; From 0e35fa615e0baf5d95dafc4362b349b8406676ab Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Wed, 12 Feb 2020 23:28:26 +0530 Subject: [PATCH 0149/2231] staging: gasket: unify multi-line string Fix three checkpatch.pl warnings of 'quoted string split across lines' in gasket_core.c by merging the strings in one line. Though some strings are over 80 characters long, fixing this warning is necessary to ease grep-ing the source for printk. Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200212175826.GA5967@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/gasket/gasket_core.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/staging/gasket/gasket_core.c b/drivers/staging/gasket/gasket_core.c index be6b50f454b4..cd181a64f737 100644 --- a/drivers/staging/gasket/gasket_core.c +++ b/drivers/staging/gasket/gasket_core.c @@ -692,8 +692,7 @@ static bool gasket_mmap_has_permissions(struct gasket_dev *gasket_dev, (vma->vm_flags & (VM_WRITE | VM_READ | VM_EXEC)); if (requested_permissions & ~(bar_permissions)) { dev_dbg(gasket_dev->dev, - "Attempting to map a region with requested permissions " - "0x%x, but region has permissions 0x%x.\n", + "Attempting to map a region with requested permissions 0x%x, but region has permissions 0x%x.\n", requested_permissions, bar_permissions); return false; } @@ -1180,8 +1179,7 @@ static int gasket_open(struct inode *inode, struct file *filp) inode->i_size = 0; dev_dbg(gasket_dev->dev, - "Attempting to open with tgid %u (%s) (f_mode: 0%03o, " - "fmode_write: %d is_root: %u)\n", + "Attempting to open with tgid %u (%s) (f_mode: 0%03o, fmode_write: %d is_root: %u)\n", current->tgid, task_name, filp->f_mode, (filp->f_mode & FMODE_WRITE), is_root); @@ -1258,8 +1256,7 @@ static int gasket_release(struct inode *inode, struct file *file) mutex_lock(&gasket_dev->mutex); dev_dbg(gasket_dev->dev, - "Releasing device node. Call origin: tgid %u (%s) " - "(f_mode: 0%03o, fmode_write: %d, is_root: %u)\n", + "Releasing device node. Call origin: tgid %u (%s) (f_mode: 0%03o, fmode_write: %d, is_root: %u)\n", current->tgid, task_name, file->f_mode, (file->f_mode & FMODE_WRITE), is_root); dev_dbg(gasket_dev->dev, "Current open count (owning tgid %u): %d\n", From 3c27a36f2711880de5e6629fbba71bfdbbf47ceb Mon Sep 17 00:00:00 2001 From: Marcelo Diop-Gonzalez Date: Wed, 12 Feb 2020 13:43:32 -0500 Subject: [PATCH 0150/2231] staging: vc04_services: use kref + RCU to reference count services Currently reference counts are implemented by locking service_spinlock and then incrementing the service's ->ref_count field, calling kfree() when the last reference has been dropped. But at the same time, there's code in multiple places that dereferences pointers to services without having a reference, so there could be a race there. It should be possible to avoid taking any lock in unlock_service() or service_release() because we are setting a single array element to NULL, and on service creation, a mutex is locked before looking for a NULL spot to put the new service in. Using a struct kref and RCU-delaying the freeing of services fixes this race condition while still making it possible to skip grabbing a reference in many places. Also it avoids the need to acquire a single spinlock when e.g. taking a reference on state->services[i] when somebody else is in the middle of taking a reference on state->services[j]. Signed-off-by: Marcelo Diop-Gonzalez Link: https://lore.kernel.org/r/3bf6f1ec6ace64d7072025505e165b8dd18b25ca.1581532523.git.marcgonzalez@google.com Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_arm.c | 25 +- .../interface/vchiq_arm/vchiq_core.c | 222 +++++++++--------- .../interface/vchiq_arm/vchiq_core.h | 12 +- 3 files changed, 140 insertions(+), 119 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index c456ced431af..3ed0e4ea7f5c 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "vchiq_core.h" @@ -2096,10 +2097,12 @@ int vchiq_dump_platform_instances(void *dump_context) /* There is no list of instances, so instead scan all services, marking those that have been dumped. */ + rcu_read_lock(); for (i = 0; i < state->unused_service; i++) { - struct vchiq_service *service = state->services[i]; + struct vchiq_service *service; struct vchiq_instance *instance; + service = rcu_dereference(state->services[i]); if (!service || service->base.callback != service_callback) continue; @@ -2107,18 +2110,26 @@ int vchiq_dump_platform_instances(void *dump_context) if (instance) instance->mark = 0; } + rcu_read_unlock(); for (i = 0; i < state->unused_service; i++) { - struct vchiq_service *service = state->services[i]; + struct vchiq_service *service; struct vchiq_instance *instance; int err; - if (!service || service->base.callback != service_callback) + rcu_read_lock(); + service = rcu_dereference(state->services[i]); + if (!service || service->base.callback != service_callback) { + rcu_read_unlock(); continue; + } instance = service->instance; - if (!instance || instance->mark) + if (!instance || instance->mark) { + rcu_read_unlock(); continue; + } + rcu_read_unlock(); len = snprintf(buf, sizeof(buf), "Instance %pK: pid %d,%s completions %d/%d", @@ -2128,7 +2139,6 @@ int vchiq_dump_platform_instances(void *dump_context) instance->completion_insert - instance->completion_remove, MAX_COMPLETIONS); - err = vchiq_dump(dump_context, buf, len + 1); if (err) return err; @@ -2585,8 +2595,10 @@ vchiq_dump_service_use_state(struct vchiq_state *state) if (active_services > MAX_SERVICES) only_nonzero = 1; + rcu_read_lock(); for (i = 0; i < active_services; i++) { - struct vchiq_service *service_ptr = state->services[i]; + struct vchiq_service *service_ptr = + rcu_dereference(state->services[i]); if (!service_ptr) continue; @@ -2604,6 +2616,7 @@ vchiq_dump_service_use_state(struct vchiq_state *state) if (found >= MAX_SERVICES) break; } + rcu_read_unlock(); read_unlock_bh(&arm_state->susp_res_lock); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index b2d9013b7f79..65270a5b29db 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright (c) 2010-2012 Broadcom. All rights reserved. */ +#include +#include + #include "vchiq_core.h" #define VCHIQ_SLOT_HANDLER_STACK 8192 @@ -54,7 +57,6 @@ int vchiq_core_log_level = VCHIQ_LOG_DEFAULT; int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT; int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT; -static DEFINE_SPINLOCK(service_spinlock); DEFINE_SPINLOCK(bulk_waiter_spinlock); static DEFINE_SPINLOCK(quota_spinlock); @@ -136,44 +138,41 @@ find_service_by_handle(unsigned int handle) { struct vchiq_service *service; - spin_lock(&service_spinlock); + rcu_read_lock(); service = handle_to_service(handle); if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && - service->handle == handle) { - WARN_ON(service->ref_count == 0); - service->ref_count++; - } else - service = NULL; - spin_unlock(&service_spinlock); - - if (!service) - vchiq_log_info(vchiq_core_log_level, - "Invalid service handle 0x%x", handle); - - return service; + service->handle == handle && + kref_get_unless_zero(&service->ref_count)) { + service = rcu_pointer_handoff(service); + rcu_read_unlock(); + return service; + } + rcu_read_unlock(); + vchiq_log_info(vchiq_core_log_level, + "Invalid service handle 0x%x", handle); + return NULL; } struct vchiq_service * find_service_by_port(struct vchiq_state *state, int localport) { - struct vchiq_service *service = NULL; if ((unsigned int)localport <= VCHIQ_PORT_MAX) { - spin_lock(&service_spinlock); - service = state->services[localport]; - if (service && service->srvstate != VCHIQ_SRVSTATE_FREE) { - WARN_ON(service->ref_count == 0); - service->ref_count++; - } else - service = NULL; - spin_unlock(&service_spinlock); + struct vchiq_service *service; + + rcu_read_lock(); + service = rcu_dereference(state->services[localport]); + if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && + kref_get_unless_zero(&service->ref_count)) { + service = rcu_pointer_handoff(service); + rcu_read_unlock(); + return service; + } + rcu_read_unlock(); } - - if (!service) - vchiq_log_info(vchiq_core_log_level, - "Invalid port %d", localport); - - return service; + vchiq_log_info(vchiq_core_log_level, + "Invalid port %d", localport); + return NULL; } struct vchiq_service * @@ -182,22 +181,20 @@ find_service_for_instance(struct vchiq_instance *instance, { struct vchiq_service *service; - spin_lock(&service_spinlock); + rcu_read_lock(); service = handle_to_service(handle); if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && service->handle == handle && - service->instance == instance) { - WARN_ON(service->ref_count == 0); - service->ref_count++; - } else - service = NULL; - spin_unlock(&service_spinlock); - - if (!service) - vchiq_log_info(vchiq_core_log_level, - "Invalid service handle 0x%x", handle); - - return service; + service->instance == instance && + kref_get_unless_zero(&service->ref_count)) { + service = rcu_pointer_handoff(service); + rcu_read_unlock(); + return service; + } + rcu_read_unlock(); + vchiq_log_info(vchiq_core_log_level, + "Invalid service handle 0x%x", handle); + return NULL; } struct vchiq_service * @@ -206,23 +203,21 @@ find_closed_service_for_instance(struct vchiq_instance *instance, { struct vchiq_service *service; - spin_lock(&service_spinlock); + rcu_read_lock(); service = handle_to_service(handle); if (service && (service->srvstate == VCHIQ_SRVSTATE_FREE || service->srvstate == VCHIQ_SRVSTATE_CLOSED) && service->handle == handle && - service->instance == instance) { - WARN_ON(service->ref_count == 0); - service->ref_count++; - } else - service = NULL; - spin_unlock(&service_spinlock); - - if (!service) - vchiq_log_info(vchiq_core_log_level, - "Invalid service handle 0x%x", handle); - + service->instance == instance && + kref_get_unless_zero(&service->ref_count)) { + service = rcu_pointer_handoff(service); + rcu_read_unlock(); + return service; + } + rcu_read_unlock(); + vchiq_log_info(vchiq_core_log_level, + "Invalid service handle 0x%x", handle); return service; } @@ -233,19 +228,19 @@ next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *insta struct vchiq_service *service = NULL; int idx = *pidx; - spin_lock(&service_spinlock); + rcu_read_lock(); while (idx < state->unused_service) { - struct vchiq_service *srv = state->services[idx++]; + struct vchiq_service *srv; + srv = rcu_dereference(state->services[idx++]); if (srv && srv->srvstate != VCHIQ_SRVSTATE_FREE && - srv->instance == instance) { - service = srv; - WARN_ON(service->ref_count == 0); - service->ref_count++; + srv->instance == instance && + kref_get_unless_zero(&srv->ref_count)) { + service = rcu_pointer_handoff(srv); break; } } - spin_unlock(&service_spinlock); + rcu_read_unlock(); *pidx = idx; @@ -255,43 +250,34 @@ next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *insta void lock_service(struct vchiq_service *service) { - spin_lock(&service_spinlock); - WARN_ON(!service); - if (service) { - WARN_ON(service->ref_count == 0); - service->ref_count++; + if (!service) { + WARN(1, "%s service is NULL\n", __func__); + return; } - spin_unlock(&service_spinlock); + kref_get(&service->ref_count); +} + +static void service_release(struct kref *kref) +{ + struct vchiq_service *service = + container_of(kref, struct vchiq_service, ref_count); + struct vchiq_state *state = service->state; + + WARN_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); + rcu_assign_pointer(state->services[service->localport], NULL); + if (service->userdata_term) + service->userdata_term(service->base.userdata); + kfree_rcu(service, rcu); } void unlock_service(struct vchiq_service *service) { - spin_lock(&service_spinlock); if (!service) { WARN(1, "%s: service is NULL\n", __func__); - goto unlock; + return; } - if (!service->ref_count) { - WARN(1, "%s: ref_count is zero\n", __func__); - goto unlock; - } - service->ref_count--; - if (!service->ref_count) { - struct vchiq_state *state = service->state; - - WARN_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); - state->services[service->localport] = NULL; - } else { - service = NULL; - } -unlock: - spin_unlock(&service_spinlock); - - if (service && service->userdata_term) - service->userdata_term(service->base.userdata); - - kfree(service); + kref_put(&service->ref_count, service_release); } int @@ -310,9 +296,14 @@ vchiq_get_client_id(unsigned int handle) void * vchiq_get_service_userdata(unsigned int handle) { - struct vchiq_service *service = handle_to_service(handle); + void *userdata; + struct vchiq_service *service; - return service ? service->base.userdata : NULL; + rcu_read_lock(); + service = handle_to_service(handle); + userdata = service ? service->base.userdata : NULL; + rcu_read_unlock(); + return userdata; } static void @@ -460,19 +451,23 @@ get_listening_service(struct vchiq_state *state, int fourcc) WARN_ON(fourcc == VCHIQ_FOURCC_INVALID); + rcu_read_lock(); for (i = 0; i < state->unused_service; i++) { - struct vchiq_service *service = state->services[i]; + struct vchiq_service *service; + service = rcu_dereference(state->services[i]); if (service && service->public_fourcc == fourcc && (service->srvstate == VCHIQ_SRVSTATE_LISTENING || (service->srvstate == VCHIQ_SRVSTATE_OPEN && - service->remoteport == VCHIQ_PORT_FREE))) { - lock_service(service); + service->remoteport == VCHIQ_PORT_FREE)) && + kref_get_unless_zero(&service->ref_count)) { + service = rcu_pointer_handoff(service); + rcu_read_unlock(); return service; } } - + rcu_read_unlock(); return NULL; } @@ -482,15 +477,20 @@ get_connected_service(struct vchiq_state *state, unsigned int port) { int i; + rcu_read_lock(); for (i = 0; i < state->unused_service; i++) { - struct vchiq_service *service = state->services[i]; + struct vchiq_service *service = + rcu_dereference(state->services[i]); if (service && service->srvstate == VCHIQ_SRVSTATE_OPEN && - service->remoteport == port) { - lock_service(service); + service->remoteport == port && + kref_get_unless_zero(&service->ref_count)) { + service = rcu_pointer_handoff(service); + rcu_read_unlock(); return service; } } + rcu_read_unlock(); return NULL; } @@ -2260,7 +2260,7 @@ vchiq_add_service_internal(struct vchiq_state *state, vchiq_userdata_term userdata_term) { struct vchiq_service *service; - struct vchiq_service **pservice = NULL; + struct vchiq_service __rcu **pservice = NULL; struct vchiq_service_quota *service_quota; int i; @@ -2272,7 +2272,7 @@ vchiq_add_service_internal(struct vchiq_state *state, service->base.callback = params->callback; service->base.userdata = params->userdata; service->handle = VCHIQ_SERVICE_HANDLE_INVALID; - service->ref_count = 1; + kref_init(&service->ref_count); service->srvstate = VCHIQ_SRVSTATE_FREE; service->userdata_term = userdata_term; service->localport = VCHIQ_PORT_FREE; @@ -2298,7 +2298,7 @@ vchiq_add_service_internal(struct vchiq_state *state, mutex_init(&service->bulk_mutex); memset(&service->stats, 0, sizeof(service->stats)); - /* Although it is perfectly possible to use service_spinlock + /* Although it is perfectly possible to use a spinlock ** to protect the creation of services, it is overkill as it ** disables interrupts while the array is searched. ** The only danger is of another thread trying to create a @@ -2316,17 +2316,17 @@ vchiq_add_service_internal(struct vchiq_state *state, if (srvstate == VCHIQ_SRVSTATE_OPENING) { for (i = 0; i < state->unused_service; i++) { - struct vchiq_service *srv = state->services[i]; - - if (!srv) { + if (!rcu_access_pointer(state->services[i])) { pservice = &state->services[i]; break; } } } else { + rcu_read_lock(); for (i = (state->unused_service - 1); i >= 0; i--) { - struct vchiq_service *srv = state->services[i]; + struct vchiq_service *srv; + srv = rcu_dereference(state->services[i]); if (!srv) pservice = &state->services[i]; else if ((srv->public_fourcc == params->fourcc) @@ -2339,6 +2339,7 @@ vchiq_add_service_internal(struct vchiq_state *state, break; } } + rcu_read_unlock(); } if (pservice) { @@ -2350,7 +2351,7 @@ vchiq_add_service_internal(struct vchiq_state *state, (state->id * VCHIQ_MAX_SERVICES) | service->localport; handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES; - *pservice = service; + rcu_assign_pointer(*pservice, service); if (pservice == &state->services[state->unused_service]) state->unused_service++; } @@ -2416,10 +2417,10 @@ vchiq_open_service_internal(struct vchiq_service *service, int client_id) (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) vchiq_log_error(vchiq_core_log_level, - "%d: osi - srvstate = %s (ref %d)", + "%d: osi - srvstate = %s (ref %u)", service->state->id, srvstate_names[service->srvstate], - service->ref_count); + kref_read(&service->ref_count)); status = VCHIQ_ERROR; VCHIQ_SERVICE_STATS_INC(service, error_count); vchiq_release_service_internal(service); @@ -3425,10 +3426,13 @@ int vchiq_dump_service_state(void *dump_context, struct vchiq_service *service) char buf[80]; int len; int err; + unsigned int ref_count; + /*Don't include the lock just taken*/ + ref_count = kref_read(&service->ref_count) - 1; len = scnprintf(buf, sizeof(buf), "Service %u: %s (ref %u)", service->localport, srvstate_names[service->srvstate], - service->ref_count - 1); /*Don't include the lock just taken*/ + ref_count); if (service->srvstate != VCHIQ_SRVSTATE_FREE) { char remoteport[30]; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 604d0c330819..30e4965c7666 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include "vchiq_cfg.h" @@ -251,7 +253,8 @@ struct vchiq_slot_info { struct vchiq_service { struct vchiq_service_base base; unsigned int handle; - unsigned int ref_count; + struct kref ref_count; + struct rcu_head rcu; int srvstate; vchiq_userdata_term userdata_term; unsigned int localport; @@ -464,7 +467,7 @@ struct vchiq_state { int error_count; } stats; - struct vchiq_service *services[VCHIQ_MAX_SERVICES]; + struct vchiq_service __rcu *services[VCHIQ_MAX_SERVICES]; struct vchiq_service_quota service_quotas[VCHIQ_MAX_SERVICES]; struct vchiq_slot_info slot_info[VCHIQ_MAX_SLOTS]; @@ -545,12 +548,13 @@ request_poll(struct vchiq_state *state, struct vchiq_service *service, static inline struct vchiq_service * handle_to_service(unsigned int handle) { + int idx = handle & (VCHIQ_MAX_SERVICES - 1); struct vchiq_state *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & (VCHIQ_MAX_STATES - 1)]; + if (!state) return NULL; - - return state->services[handle & (VCHIQ_MAX_SERVICES - 1)]; + return rcu_dereference(state->services[idx]); } extern struct vchiq_service * From a2203cfe0d847ea3a84c75ccfdd600e92d9c5e62 Mon Sep 17 00:00:00 2001 From: Marcelo Diop-Gonzalez Date: Wed, 12 Feb 2020 13:43:33 -0500 Subject: [PATCH 0151/2231] staging: vc04_services: don't increment service refcount when it's not needed There are a few places where a service's reference count is incremented, something quick is done, and the refcount is dropped. This can be made a little simpler/faster by not grabbing a reference in these cases. Signed-off-by: Marcelo Diop-Gonzalez Link: https://lore.kernel.org/r/ac6186ac888f1acf489b5b504efcba8b0d6a8b25.1581532523.git.marcgonzalez@google.com Signed-off-by: Greg Kroah-Hartman --- .../interface/vchiq_arm/vchiq_arm.c | 16 ++++----- .../interface/vchiq_arm/vchiq_core.c | 36 +++++++++++++------ .../interface/vchiq_arm/vchiq_core.h | 8 ++++- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 3ed0e4ea7f5c..b377f18aed45 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2497,11 +2497,11 @@ vchiq_instance_get_use_count(struct vchiq_instance *instance) int use_count = 0, i; i = 0; - while ((service = next_service_by_instance(instance->state, - instance, &i))) { + rcu_read_lock(); + while ((service = __next_service_by_instance(instance->state, + instance, &i))) use_count += service->service_use_count; - unlock_service(service); - } + rcu_read_unlock(); return use_count; } @@ -2524,11 +2524,11 @@ vchiq_instance_set_trace(struct vchiq_instance *instance, int trace) int i; i = 0; - while ((service = next_service_by_instance(instance->state, - instance, &i))) { + rcu_read_lock(); + while ((service = __next_service_by_instance(instance->state, + instance, &i))) service->trace = trace; - unlock_service(service); - } + rcu_read_unlock(); instance->trace = (trace != 0); } diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 65270a5b29db..d7d7f4d9d57f 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -222,28 +222,42 @@ find_closed_service_for_instance(struct vchiq_instance *instance, } struct vchiq_service * -next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *instance, - int *pidx) +__next_service_by_instance(struct vchiq_state *state, + struct vchiq_instance *instance, + int *pidx) { struct vchiq_service *service = NULL; int idx = *pidx; - rcu_read_lock(); while (idx < state->unused_service) { struct vchiq_service *srv; srv = rcu_dereference(state->services[idx++]); if (srv && srv->srvstate != VCHIQ_SRVSTATE_FREE && - srv->instance == instance && - kref_get_unless_zero(&srv->ref_count)) { - service = rcu_pointer_handoff(srv); + srv->instance == instance) { + service = srv; break; } } - rcu_read_unlock(); *pidx = idx; + return service; +} +struct vchiq_service * +next_service_by_instance(struct vchiq_state *state, + struct vchiq_instance *instance, + int *pidx) +{ + struct vchiq_service *service; + + rcu_read_lock(); + service = __next_service_by_instance(state, instance, pidx); + if (service && kref_get_unless_zero(&service->ref_count)) + service = rcu_pointer_handoff(service); + else + service = NULL; + rcu_read_unlock(); return service; } @@ -283,13 +297,13 @@ unlock_service(struct vchiq_service *service) int vchiq_get_client_id(unsigned int handle) { - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service; int id; + rcu_read_lock(); + service = handle_to_service(handle); id = service ? service->client_id : 0; - if (service) - unlock_service(service); - + rcu_read_unlock(); return id; } diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 30e4965c7666..cedd8e721aae 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -572,7 +572,13 @@ find_closed_service_for_instance(struct vchiq_instance *instance, unsigned int handle); extern struct vchiq_service * -next_service_by_instance(struct vchiq_state *state, struct vchiq_instance *instance, +__next_service_by_instance(struct vchiq_state *state, + struct vchiq_instance *instance, + int *pidx); + +extern struct vchiq_service * +next_service_by_instance(struct vchiq_state *state, + struct vchiq_instance *instance, int *pidx); extern void From 4fb8b5aa2a1126783ae00bae544d6f3c519408ef Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Wed, 12 Feb 2020 10:14:15 +0000 Subject: [PATCH 0152/2231] staging: wilc1000: refactor p2p action frames handling API's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor handling of P2P specific action frames. Make use of 'struct' to handle the P2P frames instead of manipulating using 'buf' pointer. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200212154503.8835-1-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/cfg80211.c | 306 ++++++++++++---------------- 1 file changed, 131 insertions(+), 175 deletions(-) diff --git a/drivers/staging/wilc1000/cfg80211.c b/drivers/staging/wilc1000/cfg80211.c index 7afbc475b3ea..d9c7bed2e6fb 100644 --- a/drivers/staging/wilc1000/cfg80211.c +++ b/drivers/staging/wilc1000/cfg80211.c @@ -6,29 +6,17 @@ #include "cfg80211.h" -#define FRAME_TYPE_ID 0 -#define ACTION_CAT_ID 24 -#define ACTION_SUBTYPE_ID 25 -#define P2P_PUB_ACTION_SUBTYPE 30 - -#define ACTION_FRAME 0xd0 -#define GO_INTENT_ATTR_ID 0x04 -#define CHANLIST_ATTR_ID 0x0b -#define OPERCHAN_ATTR_ID 0x11 -#define PUB_ACTION_ATTR_ID 0x04 -#define P2PELEM_ATTR_ID 0xdd - #define GO_NEG_REQ 0x00 #define GO_NEG_RSP 0x01 #define GO_NEG_CONF 0x02 #define P2P_INV_REQ 0x03 #define P2P_INV_RSP 0x04 -#define PUBLIC_ACT_VENDORSPEC 0x09 -#define GAS_INITIAL_REQ 0x0a -#define GAS_INITIAL_RSP 0x0b #define WILC_INVALID_CHANNEL 0 +/* Operation at 2.4 GHz with channels 1-13 */ +#define WILC_WLAN_OPERATING_CLASS_2_4GHZ 0x51 + static const struct ieee80211_txrx_stypes wilc_wfi_cfg80211_mgmt_types[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { @@ -67,7 +55,50 @@ struct wilc_p2p_mgmt_data { u8 *buff; }; -static const u8 p2p_oui[] = {0x50, 0x6f, 0x9A, 0x09}; +struct wilc_p2p_pub_act_frame { + u8 category; + u8 action; + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 dialog_token; + u8 elem[0]; +} __packed; + +struct wilc_vendor_specific_ie { + u8 tag_number; + u8 tag_len; + u8 oui[3]; + u8 oui_type; + u8 attr[0]; +} __packed; + +struct wilc_attr_entry { + u8 attr_type; + __le16 attr_len; + u8 val[0]; +} __packed; + +struct wilc_attr_oper_ch { + u8 attr_type; + __le16 attr_len; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u8 op_class; + u8 op_channel; +} __packed; + +struct wilc_attr_ch_list { + u8 attr_type; + __le16 attr_len; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u8 elem[0]; +} __packed; + +struct wilc_ch_list_elem { + u8 op_class; + u8 no_of_channels; + u8 ch_list[0]; +} __packed; static void cfg_scan_result(enum scan_event scan_event, struct wilc_rcvd_net_info *info, void *user_void) @@ -896,86 +927,50 @@ static int flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) return 0; } -static inline void wilc_wfi_cfg_parse_ch_attr(u8 *buf, u8 ch_list_attr_idx, - u8 op_ch_attr_idx, u8 sta_ch) +static inline void wilc_wfi_cfg_parse_ch_attr(u8 *buf, u32 len, u8 sta_ch) { - int i = 0; - int j = 0; + struct wilc_attr_entry *e; + struct wilc_attr_ch_list *ch_list; + struct wilc_attr_oper_ch *op_ch; + u32 index = 0; + u8 ch_list_idx = 0; + u8 op_ch_idx = 0; - if (ch_list_attr_idx) { - u8 limit = ch_list_attr_idx + 3 + buf[ch_list_attr_idx + 1]; + if (sta_ch == WILC_INVALID_CHANNEL) + return; - for (i = ch_list_attr_idx + 3; i < limit; i++) { - if (buf[i] == 0x51) { - for (j = i + 2; j < ((i + 2) + buf[i + 1]); j++) - buf[j] = sta_ch; + while (index + sizeof(*e) <= len) { + e = (struct wilc_attr_entry *)&buf[index]; + if (e->attr_type == IEEE80211_P2P_ATTR_CHANNEL_LIST) + ch_list_idx = index; + else if (e->attr_type == IEEE80211_P2P_ATTR_OPER_CHANNEL) + op_ch_idx = index; + if (ch_list_idx && op_ch_idx) + break; + index += le16_to_cpu(e->attr_len) + sizeof(*e); + } + + if (ch_list_idx) { + u16 attr_size; + struct wilc_ch_list_elem *e; + int i; + + ch_list = (struct wilc_attr_ch_list *)&buf[ch_list_idx]; + attr_size = le16_to_cpu(ch_list->attr_len); + for (i = 0; i < attr_size;) { + e = (struct wilc_ch_list_elem *)(ch_list->elem + i); + if (e->op_class == WILC_WLAN_OPERATING_CLASS_2_4GHZ) { + memset(e->ch_list, sta_ch, e->no_of_channels); break; } + i += e->no_of_channels; } } - if (op_ch_attr_idx) { - buf[op_ch_attr_idx + 6] = 0x51; - buf[op_ch_attr_idx + 7] = sta_ch; - } -} - -static void wilc_wfi_cfg_parse_rx_action(u8 *buf, u32 len, u8 sta_ch) -{ - u32 index = 0; - u8 op_channel_attr_index = 0; - u8 channel_list_attr_index = 0; - - while (index < len) { - if (buf[index] == CHANLIST_ATTR_ID) - channel_list_attr_index = index; - else if (buf[index] == OPERCHAN_ATTR_ID) - op_channel_attr_index = index; - index += buf[index + 1] + 3; - } - if (sta_ch != WILC_INVALID_CHANNEL) - wilc_wfi_cfg_parse_ch_attr(buf, channel_list_attr_index, - op_channel_attr_index, sta_ch); -} - -static void wilc_wfi_cfg_parse_tx_action(u8 *buf, u32 len, bool oper_ch, - u8 iftype, u8 sta_ch) -{ - u32 index = 0; - u8 op_channel_attr_index = 0; - u8 channel_list_attr_index = 0; - - while (index < len) { - if (buf[index] == CHANLIST_ATTR_ID) - channel_list_attr_index = index; - else if (buf[index] == OPERCHAN_ATTR_ID) - op_channel_attr_index = index; - index += buf[index + 1] + 3; - } - if (sta_ch != WILC_INVALID_CHANNEL && oper_ch) - wilc_wfi_cfg_parse_ch_attr(buf, channel_list_attr_index, - op_channel_attr_index, sta_ch); -} - -static void wilc_wfi_cfg_parse_rx_vendor_spec(struct wilc_priv *priv, u8 *buff, - u32 size) -{ - int i; - u8 subtype; - struct wilc_vif *vif = netdev_priv(priv->dev); - - subtype = buff[P2P_PUB_ACTION_SUBTYPE]; - if (subtype == GO_NEG_REQ || subtype == GO_NEG_RSP || - subtype == P2P_INV_REQ || subtype == P2P_INV_RSP) { - for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < size; i++) { - if (buff[i] == P2PELEM_ATTR_ID && - !(memcmp(p2p_oui, &buff[i + 2], 4))) { - wilc_wfi_cfg_parse_rx_action(&buff[i + 6], - size - (i + 6), - vif->wilc->sta_ch); - break; - } - } + if (op_ch_idx) { + op_ch = (struct wilc_attr_oper_ch *)&buf[op_ch_idx]; + op_ch->op_class = WILC_WLAN_OPERATING_CLASS_2_4GHZ; + op_ch->op_channel = sta_ch; } } @@ -984,17 +979,22 @@ void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size) struct wilc *wl = vif->wilc; struct wilc_priv *priv = &vif->priv; struct host_if_drv *wfi_drv = priv->hif_drv; + struct ieee80211_mgmt *mgmt; + struct wilc_vendor_specific_ie *p; + struct wilc_p2p_pub_act_frame *d; + int ie_offset = offsetof(struct ieee80211_mgmt, u) + sizeof(*d); + const u8 *vendor_ie; u32 header, pkt_offset; s32 freq; - __le16 fc; header = get_unaligned_le32(buff - HOST_HDR_OFFSET); pkt_offset = GET_PKT_OFFSET(header); if (pkt_offset & IS_MANAGMEMENT_CALLBACK) { bool ack = false; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)buff; - if (buff[FRAME_TYPE_ID] == IEEE80211_STYPE_PROBE_RESP || + if (ieee80211_is_probe_resp(hdr->frame_control) || pkt_offset & IS_MGMT_STATUS_SUCCES) ack = true; @@ -1005,38 +1005,33 @@ void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size) freq = ieee80211_channel_to_frequency(wl->op_ch, NL80211_BAND_2GHZ); - fc = ((struct ieee80211_hdr *)buff)->frame_control; - if (!ieee80211_is_action(fc)) { - cfg80211_rx_mgmt(&priv->wdev, freq, 0, buff, size, 0); - return; - } + mgmt = (struct ieee80211_mgmt *)buff; + if (!ieee80211_is_action(mgmt->frame_control)) + goto out_rx_mgmt; if (priv->cfg_scanning && time_after_eq(jiffies, (unsigned long)wfi_drv->p2p_timeout)) { netdev_dbg(vif->ndev, "Receiving action wrong ch\n"); return; } - if (buff[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) { - switch (buff[ACTION_SUBTYPE_ID]) { - case GAS_INITIAL_REQ: - case GAS_INITIAL_RSP: - break; - case PUBLIC_ACT_VENDORSPEC: - if (!memcmp(p2p_oui, &buff[ACTION_SUBTYPE_ID + 1], 4)) - wilc_wfi_cfg_parse_rx_vendor_spec(priv, buff, - size); + if (!ieee80211_is_public_action((struct ieee80211_hdr *)buff, size)) + goto out_rx_mgmt; - break; + d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action); + if (d->oui_subtype != GO_NEG_REQ && d->oui_subtype != GO_NEG_RSP && + d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP) + goto out_rx_mgmt; - default: - netdev_dbg(vif->ndev, - "%s: Not handled action frame type:%x\n", - __func__, buff[ACTION_SUBTYPE_ID]); - break; - } - } + vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, + buff + ie_offset, size - ie_offset); + if (!vendor_ie) + goto out_rx_mgmt; + p = (struct wilc_vendor_specific_ie *)vendor_ie; + wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, vif->wilc->sta_ch); + +out_rx_mgmt: cfg80211_rx_mgmt(&priv->wdev, freq, 0, buff, size, 0); } @@ -1116,39 +1111,6 @@ static int cancel_remain_on_channel(struct wiphy *wiphy, return wilc_listen_state_expired(vif, cookie); } -static void wilc_wfi_cfg_tx_vendor_spec(struct wilc_priv *priv, - struct wilc_p2p_mgmt_data *mgmt_tx, - struct cfg80211_mgmt_tx_params *params, - u8 iftype, u32 buf_len) -{ - const u8 *buf = params->buf; - size_t len = params->len; - u32 i; - u8 subtype = buf[P2P_PUB_ACTION_SUBTYPE]; - struct wilc_vif *vif = netdev_priv(priv->dev); - - if (subtype != GO_NEG_REQ && subtype != GO_NEG_RSP && - subtype != P2P_INV_REQ && subtype != P2P_INV_RSP) - return; - - for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < len; i++) { - if (buf[i] == P2PELEM_ATTR_ID && - !memcmp(p2p_oui, &buf[i + 2], 4)) { - bool oper_ch = false; - u8 *tx_buff = &mgmt_tx->buff[i + 6]; - - if (subtype == P2P_INV_REQ || subtype == P2P_INV_RSP) - oper_ch = true; - - wilc_wfi_cfg_parse_tx_action(tx_buff, len - (i + 6), - oper_ch, iftype, - vif->wilc->sta_ch); - - break; - } - } -} - static int mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, @@ -1163,6 +1125,10 @@ static int mgmt_tx(struct wiphy *wiphy, struct wilc_vif *vif = netdev_priv(wdev->netdev); struct wilc_priv *priv = &vif->priv; struct host_if_drv *wfi_drv = priv->hif_drv; + struct wilc_vendor_specific_ie *p; + struct wilc_p2p_pub_act_frame *d; + int ie_offset = offsetof(struct ieee80211_mgmt, u) + sizeof(*d); + const u8 *vendor_ie; int ret = 0; *cookie = prandom_u32(); @@ -1194,39 +1160,29 @@ static int mgmt_tx(struct wiphy *wiphy, goto out_txq_add_pkt; } - if (!ieee80211_is_action(mgmt->frame_control)) - goto out_txq_add_pkt; + if (!ieee80211_is_public_action((struct ieee80211_hdr *)buf, len)) + goto out_set_timeout; - if (buf[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) { - if (buf[ACTION_SUBTYPE_ID] != PUBLIC_ACT_VENDORSPEC || - buf[P2P_PUB_ACTION_SUBTYPE] != GO_NEG_CONF) { - wilc_set_mac_chnl_num(vif, chan->hw_value); - vif->wilc->op_ch = chan->hw_value; - } - switch (buf[ACTION_SUBTYPE_ID]) { - case GAS_INITIAL_REQ: - case GAS_INITIAL_RSP: - break; - - case PUBLIC_ACT_VENDORSPEC: - if (!memcmp(p2p_oui, &buf[ACTION_SUBTYPE_ID + 1], 4)) - wilc_wfi_cfg_tx_vendor_spec(priv, mgmt_tx, - params, vif->iftype, - len); - else - netdev_dbg(vif->ndev, - "Not a P2P public action frame\n"); - - break; - - default: - netdev_dbg(vif->ndev, - "%s: Not handled action frame type:%x\n", - __func__, buf[ACTION_SUBTYPE_ID]); - break; - } + d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action); + if (d->oui_type != WLAN_OUI_TYPE_WFA_P2P || + d->oui_subtype != GO_NEG_CONF) { + wilc_set_mac_chnl_num(vif, chan->hw_value); + vif->wilc->op_ch = chan->hw_value; } + if (d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP) + goto out_set_timeout; + + vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, + mgmt_tx->buff + ie_offset, + len - ie_offset); + if (!vendor_ie) + goto out_set_timeout; + + p = (struct wilc_vendor_specific_ie *)vendor_ie; + wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, vif->wilc->sta_ch); + +out_set_timeout: wfi_drv->p2p_timeout = (jiffies + msecs_to_jiffies(wait)); out_txq_add_pkt: From caa6772db4c1deb5d9add48e95d6eab50699ee5e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 10 Feb 2020 15:14:17 -0800 Subject: [PATCH 0153/2231] Staging: remove wusbcore and UWB from the kernel tree. It's been over 6 months, and no one has noticed that these drivers are deleted, probably because no one actually has this hardware. As no one has volunteered to maintain the code, let's drop it for good. Link: https://lore.kernel.org/r/20200210231417.GA1736729@kroah.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 10 - drivers/staging/Kconfig | 3 - drivers/staging/Makefile | 2 - drivers/staging/uwb/Kconfig | 72 - drivers/staging/uwb/Makefile | 32 - drivers/staging/uwb/TODO | 8 - drivers/staging/uwb/address.c | 352 -- drivers/staging/uwb/allocator.c | 374 --- drivers/staging/uwb/beacon.c | 595 ---- drivers/staging/uwb/driver.c | 143 - drivers/staging/uwb/drp-avail.c | 278 -- drivers/staging/uwb/drp-ie.c | 305 -- drivers/staging/uwb/drp.c | 842 ----- drivers/staging/uwb/est.c | 450 --- drivers/staging/uwb/hwa-rc.c | 929 ------ drivers/staging/uwb/i1480/Makefile | 2 - drivers/staging/uwb/i1480/dfu/Makefile | 10 - drivers/staging/uwb/i1480/dfu/dfu.c | 198 -- drivers/staging/uwb/i1480/dfu/i1480-dfu.h | 246 -- drivers/staging/uwb/i1480/dfu/mac.c | 496 --- drivers/staging/uwb/i1480/dfu/phy.c | 190 -- drivers/staging/uwb/i1480/dfu/usb.c | 448 --- drivers/staging/uwb/i1480/i1480-est.c | 85 - drivers/staging/uwb/ie-rcv.c | 42 - drivers/staging/uwb/ie.c | 366 --- drivers/staging/uwb/include/debug-cmd.h | 57 - drivers/staging/uwb/include/spec.h | 767 ----- drivers/staging/uwb/include/umc.h | 192 -- drivers/staging/uwb/include/whci.h | 102 - drivers/staging/uwb/lc-dev.c | 457 --- drivers/staging/uwb/lc-rc.c | 569 ---- drivers/staging/uwb/neh.c | 606 ---- drivers/staging/uwb/pal.c | 128 - drivers/staging/uwb/radio.c | 196 -- drivers/staging/uwb/reset.c | 379 --- drivers/staging/uwb/rsv.c | 1000 ------ drivers/staging/uwb/scan.c | 120 - drivers/staging/uwb/umc-bus.c | 211 -- drivers/staging/uwb/umc-dev.c | 94 - drivers/staging/uwb/umc-drv.c | 31 - drivers/staging/uwb/uwb-debug.c | 354 -- drivers/staging/uwb/uwb-internal.h | 366 --- drivers/staging/uwb/uwb.h | 817 ----- drivers/staging/uwb/uwbd.c | 356 -- drivers/staging/uwb/whc-rc.c | 467 --- drivers/staging/uwb/whci.c | 257 -- .../staging/wusbcore/Documentation/wusb-cbaf | 130 - .../Documentation/wusb-design-overview.rst | 457 --- drivers/staging/wusbcore/Kconfig | 39 - drivers/staging/wusbcore/Makefile | 28 - drivers/staging/wusbcore/TODO | 8 - drivers/staging/wusbcore/cbaf.c | 645 ---- drivers/staging/wusbcore/crypto.c | 441 --- drivers/staging/wusbcore/dev-sysfs.c | 124 - drivers/staging/wusbcore/devconnect.c | 1085 ------ drivers/staging/wusbcore/host/Kconfig | 28 - drivers/staging/wusbcore/host/Makefile | 3 - drivers/staging/wusbcore/host/hwa-hc.c | 875 ----- drivers/staging/wusbcore/host/whci/Makefile | 14 - drivers/staging/wusbcore/host/whci/asl.c | 376 --- drivers/staging/wusbcore/host/whci/debug.c | 153 - drivers/staging/wusbcore/host/whci/hcd.c | 356 -- drivers/staging/wusbcore/host/whci/hw.c | 93 - drivers/staging/wusbcore/host/whci/init.c | 177 - drivers/staging/wusbcore/host/whci/int.c | 82 - drivers/staging/wusbcore/host/whci/pzl.c | 404 --- drivers/staging/wusbcore/host/whci/qset.c | 831 ----- drivers/staging/wusbcore/host/whci/whcd.h | 202 -- drivers/staging/wusbcore/host/whci/whci-hc.h | 401 --- drivers/staging/wusbcore/host/whci/wusb.c | 210 -- .../staging/wusbcore/include/association.h | 151 - drivers/staging/wusbcore/include/wusb-wa.h | 304 -- drivers/staging/wusbcore/include/wusb.h | 362 -- drivers/staging/wusbcore/mmc.c | 303 -- drivers/staging/wusbcore/pal.c | 45 - drivers/staging/wusbcore/reservation.c | 110 - drivers/staging/wusbcore/rh.c | 426 --- drivers/staging/wusbcore/security.c | 599 ---- drivers/staging/wusbcore/wa-hc.c | 88 - drivers/staging/wusbcore/wa-hc.h | 467 --- drivers/staging/wusbcore/wa-nep.c | 289 -- drivers/staging/wusbcore/wa-rpipe.c | 539 --- drivers/staging/wusbcore/wa-xfer.c | 2927 ----------------- drivers/staging/wusbcore/wusbhc.c | 490 --- drivers/staging/wusbcore/wusbhc.h | 487 --- 85 files changed, 28753 deletions(-) delete mode 100644 drivers/staging/uwb/Kconfig delete mode 100644 drivers/staging/uwb/Makefile delete mode 100644 drivers/staging/uwb/TODO delete mode 100644 drivers/staging/uwb/address.c delete mode 100644 drivers/staging/uwb/allocator.c delete mode 100644 drivers/staging/uwb/beacon.c delete mode 100644 drivers/staging/uwb/driver.c delete mode 100644 drivers/staging/uwb/drp-avail.c delete mode 100644 drivers/staging/uwb/drp-ie.c delete mode 100644 drivers/staging/uwb/drp.c delete mode 100644 drivers/staging/uwb/est.c delete mode 100644 drivers/staging/uwb/hwa-rc.c delete mode 100644 drivers/staging/uwb/i1480/Makefile delete mode 100644 drivers/staging/uwb/i1480/dfu/Makefile delete mode 100644 drivers/staging/uwb/i1480/dfu/dfu.c delete mode 100644 drivers/staging/uwb/i1480/dfu/i1480-dfu.h delete mode 100644 drivers/staging/uwb/i1480/dfu/mac.c delete mode 100644 drivers/staging/uwb/i1480/dfu/phy.c delete mode 100644 drivers/staging/uwb/i1480/dfu/usb.c delete mode 100644 drivers/staging/uwb/i1480/i1480-est.c delete mode 100644 drivers/staging/uwb/ie-rcv.c delete mode 100644 drivers/staging/uwb/ie.c delete mode 100644 drivers/staging/uwb/include/debug-cmd.h delete mode 100644 drivers/staging/uwb/include/spec.h delete mode 100644 drivers/staging/uwb/include/umc.h delete mode 100644 drivers/staging/uwb/include/whci.h delete mode 100644 drivers/staging/uwb/lc-dev.c delete mode 100644 drivers/staging/uwb/lc-rc.c delete mode 100644 drivers/staging/uwb/neh.c delete mode 100644 drivers/staging/uwb/pal.c delete mode 100644 drivers/staging/uwb/radio.c delete mode 100644 drivers/staging/uwb/reset.c delete mode 100644 drivers/staging/uwb/rsv.c delete mode 100644 drivers/staging/uwb/scan.c delete mode 100644 drivers/staging/uwb/umc-bus.c delete mode 100644 drivers/staging/uwb/umc-dev.c delete mode 100644 drivers/staging/uwb/umc-drv.c delete mode 100644 drivers/staging/uwb/uwb-debug.c delete mode 100644 drivers/staging/uwb/uwb-internal.h delete mode 100644 drivers/staging/uwb/uwb.h delete mode 100644 drivers/staging/uwb/uwbd.c delete mode 100644 drivers/staging/uwb/whc-rc.c delete mode 100644 drivers/staging/uwb/whci.c delete mode 100644 drivers/staging/wusbcore/Documentation/wusb-cbaf delete mode 100644 drivers/staging/wusbcore/Documentation/wusb-design-overview.rst delete mode 100644 drivers/staging/wusbcore/Kconfig delete mode 100644 drivers/staging/wusbcore/Makefile delete mode 100644 drivers/staging/wusbcore/TODO delete mode 100644 drivers/staging/wusbcore/cbaf.c delete mode 100644 drivers/staging/wusbcore/crypto.c delete mode 100644 drivers/staging/wusbcore/dev-sysfs.c delete mode 100644 drivers/staging/wusbcore/devconnect.c delete mode 100644 drivers/staging/wusbcore/host/Kconfig delete mode 100644 drivers/staging/wusbcore/host/Makefile delete mode 100644 drivers/staging/wusbcore/host/hwa-hc.c delete mode 100644 drivers/staging/wusbcore/host/whci/Makefile delete mode 100644 drivers/staging/wusbcore/host/whci/asl.c delete mode 100644 drivers/staging/wusbcore/host/whci/debug.c delete mode 100644 drivers/staging/wusbcore/host/whci/hcd.c delete mode 100644 drivers/staging/wusbcore/host/whci/hw.c delete mode 100644 drivers/staging/wusbcore/host/whci/init.c delete mode 100644 drivers/staging/wusbcore/host/whci/int.c delete mode 100644 drivers/staging/wusbcore/host/whci/pzl.c delete mode 100644 drivers/staging/wusbcore/host/whci/qset.c delete mode 100644 drivers/staging/wusbcore/host/whci/whcd.h delete mode 100644 drivers/staging/wusbcore/host/whci/whci-hc.h delete mode 100644 drivers/staging/wusbcore/host/whci/wusb.c delete mode 100644 drivers/staging/wusbcore/include/association.h delete mode 100644 drivers/staging/wusbcore/include/wusb-wa.h delete mode 100644 drivers/staging/wusbcore/include/wusb.h delete mode 100644 drivers/staging/wusbcore/mmc.c delete mode 100644 drivers/staging/wusbcore/pal.c delete mode 100644 drivers/staging/wusbcore/reservation.c delete mode 100644 drivers/staging/wusbcore/rh.c delete mode 100644 drivers/staging/wusbcore/security.c delete mode 100644 drivers/staging/wusbcore/wa-hc.c delete mode 100644 drivers/staging/wusbcore/wa-hc.h delete mode 100644 drivers/staging/wusbcore/wa-nep.c delete mode 100644 drivers/staging/wusbcore/wa-rpipe.c delete mode 100644 drivers/staging/wusbcore/wa-xfer.c delete mode 100644 drivers/staging/wusbcore/wusbhc.c delete mode 100644 drivers/staging/wusbcore/wusbhc.h diff --git a/MAINTAINERS b/MAINTAINERS index 38fe2f3f7b6f..9a4c715d1e50 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3919,11 +3919,6 @@ F: certs/ F: scripts/sign-file.c F: scripts/extract-cert.c -CERTIFIED WIRELESS USB (WUSB) SUBSYSTEM: -L: devel@driverdev.osuosl.org -S: Obsolete -F: drivers/staging/wusbcore/ - CFAG12864B LCD DRIVER M: Miguel Ojeda Sandonis S: Maintained @@ -17094,11 +17089,6 @@ S: Maintained F: drivers/usb/common/ulpi.c F: include/linux/ulpi/ -ULTRA-WIDEBAND (UWB) SUBSYSTEM: -L: devel@driverdev.osuosl.org -S: Obsolete -F: drivers/staging/uwb/ - UNICODE SUBSYSTEM: M: Gabriel Krisman Bertazi L: linux-fsdevel@vger.kernel.org diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index baccd7c883cc..0f82e23e151b 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -112,9 +112,6 @@ source "drivers/staging/fieldbus/Kconfig" source "drivers/staging/kpc2000/Kconfig" -source "drivers/staging/wusbcore/Kconfig" -source "drivers/staging/uwb/Kconfig" - source "drivers/staging/exfat/Kconfig" source "drivers/staging/qlge/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index fdd03fd6e704..49b21951b6f2 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -46,8 +46,6 @@ obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/ obj-$(CONFIG_KPC2000) += kpc2000/ -obj-$(CONFIG_UWB) += uwb/ -obj-$(CONFIG_USB_WUSB) += wusbcore/ obj-$(CONFIG_STAGING_EXFAT_FS) += exfat/ obj-$(CONFIG_QLGE) += qlge/ obj-$(CONFIG_NET_VENDOR_HP) += hp/ diff --git a/drivers/staging/uwb/Kconfig b/drivers/staging/uwb/Kconfig deleted file mode 100644 index 259e053e1e09..000000000000 --- a/drivers/staging/uwb/Kconfig +++ /dev/null @@ -1,72 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# UWB device configuration -# - -menuconfig UWB - tristate "Ultra Wideband devices" - default n - select GENERIC_NET_UTILS - help - UWB is a high-bandwidth, low-power, point-to-point radio - technology using a wide spectrum (3.1-10.6GHz). It is - optimized for in-room use (480Mbps at 2 meters, 110Mbps at - 10m). It serves as the transport layer for other protocols, - such as Wireless USB (WUSB). - - The topology is peer to peer; however, higher level - protocols (such as WUSB) might impose a master/slave - relationship. - - Say Y here if your computer has UWB radio controllers (USB or PCI) - based. You will need to enable the radio controllers - below. It is ok to select all of them, no harm done. - - For more help check the UWB and WUSB related files in - . - - To compile the UWB stack as a module, choose M here. - -if UWB - -config UWB_HWA - tristate "UWB Radio Control driver for WUSB-compliant USB dongles (HWA)" - depends on USB - help - This driver enables the radio controller for HWA USB - devices. HWA stands for Host Wire Adapter, and it is a UWB - Radio Controller connected to your system via USB. Most of - them come with a Wireless USB host controller also. - - To compile this driver select Y (built in) or M (module). It - is safe to select any even if you do not have the hardware. - -config UWB_WHCI - tristate "UWB Radio Control driver for WHCI-compliant cards" - depends on PCI - help - This driver enables the radio controller for WHCI cards. - - WHCI is a specification developed by Intel - (http://www.intel.com/technology/comms/wusb/whci.htm) much - in the spirit of USB's EHCI, but for UWB and Wireless USB - radio/host controllers connected via memory mapping (eg: - PCI). Most of these cards come also with a Wireless USB host - controller. - - To compile this driver select Y (built in) or M (module). It - is safe to select any even if you do not have the hardware. - -config UWB_I1480U - tristate "Support for Intel Wireless UWB Link 1480 HWA" - depends on UWB_HWA - select FW_LOADER - help - This driver enables support for the i1480 when connected via - USB. It consists of a firmware uploader that will enable it - to behave as an HWA device. - - To compile this driver select Y (built in) or M (module). It - is safe to select any even if you do not have the hardware. - -endif # UWB diff --git a/drivers/staging/uwb/Makefile b/drivers/staging/uwb/Makefile deleted file mode 100644 index 32f4de7afbd6..000000000000 --- a/drivers/staging/uwb/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_UWB) += uwb.o -obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o -obj-$(CONFIG_UWB_HWA) += hwa-rc.o -obj-$(CONFIG_UWB_I1480U) += i1480/ - -uwb-objs := \ - address.o \ - allocator.o \ - beacon.o \ - driver.o \ - drp.o \ - drp-avail.o \ - drp-ie.o \ - est.o \ - ie.o \ - ie-rcv.o \ - lc-dev.o \ - lc-rc.o \ - neh.o \ - pal.o \ - radio.o \ - reset.o \ - rsv.o \ - scan.o \ - uwb-debug.o \ - uwbd.o - -umc-objs := \ - umc-bus.o \ - umc-dev.o \ - umc-drv.o diff --git a/drivers/staging/uwb/TODO b/drivers/staging/uwb/TODO deleted file mode 100644 index abae57000534..000000000000 --- a/drivers/staging/uwb/TODO +++ /dev/null @@ -1,8 +0,0 @@ -TODO: Remove in late 2019 unless there are users - -There seems to not be any real wireless USB devices anywhere in the wild -anymore. It turned out to be a failed technology :( - -This will be removed from the tree if no one objects. - -Greg Kroah-Hartman diff --git a/drivers/staging/uwb/address.c b/drivers/staging/uwb/address.c deleted file mode 100644 index 857d5cd56a95..000000000000 --- a/drivers/staging/uwb/address.c +++ /dev/null @@ -1,352 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Address management - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - */ - -#include -#include -#include -#include -#include -#include - -#include "uwb-internal.h" - - -/** Device Address Management command */ -struct uwb_rc_cmd_dev_addr_mgmt { - struct uwb_rccb rccb; - u8 bmOperationType; - u8 baAddr[6]; -} __attribute__((packed)); - - -/** - * Low level command for setting/getting UWB radio's addresses - * - * @hwarc: HWA Radio Control interface instance - * @bmOperationType: - * Set/get, MAC/DEV (see WUSB1.0[8.6.2.2]) - * @baAddr: address buffer--assumed to have enough data to hold - * the address type requested. - * @reply: Pointer to reply buffer (can be stack allocated) - * @returns: 0 if ok, < 0 errno code on error. - * - * @cmd has to be allocated because USB cannot grok USB or vmalloc - * buffers depending on your combination of host architecture. - */ -static -int uwb_rc_dev_addr_mgmt(struct uwb_rc *rc, - u8 bmOperationType, const u8 *baAddr, - struct uwb_rc_evt_dev_addr_mgmt *reply) -{ - int result; - struct uwb_rc_cmd_dev_addr_mgmt *cmd; - - result = -ENOMEM; - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - goto error_kzalloc; - cmd->rccb.bCommandType = UWB_RC_CET_GENERAL; - cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_DEV_ADDR_MGMT); - cmd->bmOperationType = bmOperationType; - if (baAddr) { - size_t size = 0; - switch (bmOperationType >> 1) { - case 0: size = 2; break; - case 1: size = 6; break; - default: BUG(); - } - memcpy(cmd->baAddr, baAddr, size); - } - reply->rceb.bEventType = UWB_RC_CET_GENERAL; - reply->rceb.wEvent = UWB_RC_CMD_DEV_ADDR_MGMT; - result = uwb_rc_cmd(rc, "DEV-ADDR-MGMT", - &cmd->rccb, sizeof(*cmd), - &reply->rceb, sizeof(*reply)); - if (result < 0) - goto error_cmd; - if (result < sizeof(*reply)) { - dev_err(&rc->uwb_dev.dev, - "DEV-ADDR-MGMT: not enough data replied: " - "%d vs %zu bytes needed\n", result, sizeof(*reply)); - result = -ENOMSG; - } else if (reply->bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(&rc->uwb_dev.dev, - "DEV-ADDR-MGMT: command execution failed: %s (%d)\n", - uwb_rc_strerror(reply->bResultCode), - reply->bResultCode); - result = -EIO; - } else - result = 0; -error_cmd: - kfree(cmd); -error_kzalloc: - return result; -} - - -/** - * Set the UWB RC MAC or device address. - * - * @rc: UWB Radio Controller - * @_addr: Pointer to address to write [assumed to be either a - * 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *']. - * @type: Type of address to set (UWB_ADDR_DEV or UWB_ADDR_MAC). - * @returns: 0 if ok, < 0 errno code on error. - * - * Some anal retentivity here: even if both 'struct - * uwb_{dev,mac}_addr' have the actual byte array in the same offset - * and I could just pass _addr to hwarc_cmd_dev_addr_mgmt(), I prefer - * to use some syntatic sugar in case someday we decide to change the - * format of the structs. The compiler will optimize it out anyway. - */ -static int uwb_rc_addr_set(struct uwb_rc *rc, - const void *_addr, enum uwb_addr_type type) -{ - int result; - u8 bmOperationType = 0x1; /* Set address */ - const struct uwb_dev_addr *dev_addr = _addr; - const struct uwb_mac_addr *mac_addr = _addr; - struct uwb_rc_evt_dev_addr_mgmt reply; - const u8 *baAddr; - - result = -EINVAL; - switch (type) { - case UWB_ADDR_DEV: - baAddr = dev_addr->data; - break; - case UWB_ADDR_MAC: - baAddr = mac_addr->data; - bmOperationType |= 0x2; - break; - default: - return result; - } - return uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &reply); -} - - -/** - * Get the UWB radio's MAC or device address. - * - * @rc: UWB Radio Controller - * @_addr: Where to write the address data [assumed to be either a - * 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *']. - * @type: Type of address to get (UWB_ADDR_DEV or UWB_ADDR_MAC). - * @returns: 0 if ok (and *_addr set), < 0 errno code on error. - * - * See comment in uwb_rc_addr_set() about anal retentivity in the - * type handling of the address variables. - */ -static int uwb_rc_addr_get(struct uwb_rc *rc, - void *_addr, enum uwb_addr_type type) -{ - int result; - u8 bmOperationType = 0x0; /* Get address */ - struct uwb_rc_evt_dev_addr_mgmt evt; - struct uwb_dev_addr *dev_addr = _addr; - struct uwb_mac_addr *mac_addr = _addr; - u8 *baAddr; - - result = -EINVAL; - switch (type) { - case UWB_ADDR_DEV: - baAddr = dev_addr->data; - break; - case UWB_ADDR_MAC: - bmOperationType |= 0x2; - baAddr = mac_addr->data; - break; - default: - return result; - } - result = uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &evt); - if (result == 0) - switch (type) { - case UWB_ADDR_DEV: - memcpy(&dev_addr->data, evt.baAddr, - sizeof(dev_addr->data)); - break; - case UWB_ADDR_MAC: - memcpy(&mac_addr->data, evt.baAddr, - sizeof(mac_addr->data)); - break; - default: /* shut gcc up */ - BUG(); - } - return result; -} - - -/** Get @rc's MAC address to @addr */ -int uwb_rc_mac_addr_get(struct uwb_rc *rc, - struct uwb_mac_addr *addr) { - return uwb_rc_addr_get(rc, addr, UWB_ADDR_MAC); -} -EXPORT_SYMBOL_GPL(uwb_rc_mac_addr_get); - - -/** Get @rc's device address to @addr */ -int uwb_rc_dev_addr_get(struct uwb_rc *rc, - struct uwb_dev_addr *addr) { - return uwb_rc_addr_get(rc, addr, UWB_ADDR_DEV); -} -EXPORT_SYMBOL_GPL(uwb_rc_dev_addr_get); - - -/** Set @rc's address to @addr */ -int uwb_rc_mac_addr_set(struct uwb_rc *rc, - const struct uwb_mac_addr *addr) -{ - int result = -EINVAL; - mutex_lock(&rc->uwb_dev.mutex); - result = uwb_rc_addr_set(rc, addr, UWB_ADDR_MAC); - mutex_unlock(&rc->uwb_dev.mutex); - return result; -} - - -/** Set @rc's address to @addr */ -int uwb_rc_dev_addr_set(struct uwb_rc *rc, - const struct uwb_dev_addr *addr) -{ - int result = -EINVAL; - mutex_lock(&rc->uwb_dev.mutex); - result = uwb_rc_addr_set(rc, addr, UWB_ADDR_DEV); - rc->uwb_dev.dev_addr = *addr; - mutex_unlock(&rc->uwb_dev.mutex); - return result; -} - -/* Returns !0 if given address is already assigned to device. */ -int __uwb_mac_addr_assigned_check(struct device *dev, void *_addr) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_mac_addr *addr = _addr; - - if (!uwb_mac_addr_cmp(addr, &uwb_dev->mac_addr)) - return !0; - return 0; -} - -/* Returns !0 if given address is already assigned to device. */ -int __uwb_dev_addr_assigned_check(struct device *dev, void *_addr) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_dev_addr *addr = _addr; - if (!uwb_dev_addr_cmp(addr, &uwb_dev->dev_addr)) - return !0; - return 0; -} - -/** - * uwb_dev_addr_assign - assigned a generated DevAddr to a radio controller - * @rc: the (local) radio controller device requiring a new DevAddr - * - * A new DevAddr is required when: - * - first setting up a radio controller - * - if the hardware reports a DevAddr conflict - * - * The DevAddr is randomly generated in the generated DevAddr range - * [0x100, 0xfeff]. The number of devices in a beacon group is limited - * by mMaxBPLength (96) so this address space will never be exhausted. - * - * [ECMA-368] 17.1.1, 17.16. - */ -int uwb_rc_dev_addr_assign(struct uwb_rc *rc) -{ - struct uwb_dev_addr new_addr; - - do { - get_random_bytes(new_addr.data, sizeof(new_addr.data)); - } while (new_addr.data[0] == 0x00 || new_addr.data[0] == 0xff - || __uwb_dev_addr_assigned(rc, &new_addr)); - - return uwb_rc_dev_addr_set(rc, &new_addr); -} - -/** - * uwbd_evt_handle_rc_dev_addr_conflict - handle a DEV_ADDR_CONFLICT event - * @evt: the DEV_ADDR_CONFLICT notification from the radio controller - * - * A new (non-conflicting) DevAddr is assigned to the radio controller. - * - * [ECMA-368] 17.1.1.1. - */ -int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt) -{ - struct uwb_rc *rc = evt->rc; - - return uwb_rc_dev_addr_assign(rc); -} - -/* - * Print the 48-bit EUI MAC address of the radio controller when - * reading /sys/class/uwb_rc/XX/mac_address - */ -static ssize_t uwb_rc_mac_addr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_rc *rc = uwb_dev->rc; - struct uwb_mac_addr addr; - ssize_t result; - - mutex_lock(&rc->uwb_dev.mutex); - result = uwb_rc_addr_get(rc, &addr, UWB_ADDR_MAC); - mutex_unlock(&rc->uwb_dev.mutex); - if (result >= 0) { - result = uwb_mac_addr_print(buf, UWB_ADDR_STRSIZE, &addr); - buf[result++] = '\n'; - } - return result; -} - -/* - * Parse a 48 bit address written to /sys/class/uwb_rc/XX/mac_address - * and if correct, set it. - */ -static ssize_t uwb_rc_mac_addr_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_rc *rc = uwb_dev->rc; - struct uwb_mac_addr addr; - ssize_t result; - - if (!mac_pton(buf, addr.data)) - return -EINVAL; - if (is_multicast_ether_addr(addr.data)) { - dev_err(&rc->uwb_dev.dev, "refusing to set multicast " - "MAC address %s\n", buf); - return -EINVAL; - } - result = uwb_rc_mac_addr_set(rc, &addr); - if (result == 0) - rc->uwb_dev.mac_addr = addr; - - return result < 0 ? result : size; -} -DEVICE_ATTR(mac_address, S_IRUGO | S_IWUSR, uwb_rc_mac_addr_show, uwb_rc_mac_addr_store); - -/** Print @addr to @buf, @return bytes written */ -size_t __uwb_addr_print(char *buf, size_t buf_size, const unsigned char *addr, - int type) -{ - size_t result; - if (type) - result = scnprintf(buf, buf_size, "%pM", addr); - else - result = scnprintf(buf, buf_size, "%02x:%02x", - addr[1], addr[0]); - return result; -} -EXPORT_SYMBOL_GPL(__uwb_addr_print); diff --git a/drivers/staging/uwb/allocator.c b/drivers/staging/uwb/allocator.c deleted file mode 100644 index 1f429fba20b7..000000000000 --- a/drivers/staging/uwb/allocator.c +++ /dev/null @@ -1,374 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * UWB reservation management. - * - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include "uwb.h" - -#include "uwb-internal.h" - -static void uwb_rsv_fill_column_alloc(struct uwb_rsv_alloc_info *ai) -{ - int col, mas, safe_mas, unsafe_mas; - unsigned char *bm = ai->bm; - struct uwb_rsv_col_info *ci = ai->ci; - unsigned char c; - - for (col = ci->csi.start_col; col < UWB_NUM_ZONES; col += ci->csi.interval) { - - safe_mas = ci->csi.safe_mas_per_col; - unsafe_mas = ci->csi.unsafe_mas_per_col; - - for (mas = 0; mas < UWB_MAS_PER_ZONE; mas++ ) { - if (bm[col * UWB_MAS_PER_ZONE + mas] == 0) { - - if (safe_mas > 0) { - safe_mas--; - c = UWB_RSV_MAS_SAFE; - } else if (unsafe_mas > 0) { - unsafe_mas--; - c = UWB_RSV_MAS_UNSAFE; - } else { - break; - } - bm[col * UWB_MAS_PER_ZONE + mas] = c; - } - } - } -} - -static void uwb_rsv_fill_row_alloc(struct uwb_rsv_alloc_info *ai) -{ - int mas, col, rows; - unsigned char *bm = ai->bm; - struct uwb_rsv_row_info *ri = &ai->ri; - unsigned char c; - - rows = 1; - c = UWB_RSV_MAS_SAFE; - for (mas = UWB_MAS_PER_ZONE - 1; mas >= 0; mas--) { - if (ri->avail[mas] == 1) { - - if (rows > ri->used_rows) { - break; - } else if (rows > 7) { - c = UWB_RSV_MAS_UNSAFE; - } - - for (col = 0; col < UWB_NUM_ZONES; col++) { - if (bm[col * UWB_NUM_ZONES + mas] != UWB_RSV_MAS_NOT_AVAIL) { - bm[col * UWB_NUM_ZONES + mas] = c; - if(c == UWB_RSV_MAS_SAFE) - ai->safe_allocated_mases++; - else - ai->unsafe_allocated_mases++; - } - } - rows++; - } - } - ai->total_allocated_mases = ai->safe_allocated_mases + ai->unsafe_allocated_mases; -} - -/* - * Find the best column set for a given availability, interval, num safe mas and - * num unsafe mas. - * - * The different sets are tried in order as shown below, depending on the interval. - * - * interval = 16 - * deep = 0 - * set 1 -> { 8 } - * deep = 1 - * set 1 -> { 4 } - * set 2 -> { 12 } - * deep = 2 - * set 1 -> { 2 } - * set 2 -> { 6 } - * set 3 -> { 10 } - * set 4 -> { 14 } - * deep = 3 - * set 1 -> { 1 } - * set 2 -> { 3 } - * set 3 -> { 5 } - * set 4 -> { 7 } - * set 5 -> { 9 } - * set 6 -> { 11 } - * set 7 -> { 13 } - * set 8 -> { 15 } - * - * interval = 8 - * deep = 0 - * set 1 -> { 4 12 } - * deep = 1 - * set 1 -> { 2 10 } - * set 2 -> { 6 14 } - * deep = 2 - * set 1 -> { 1 9 } - * set 2 -> { 3 11 } - * set 3 -> { 5 13 } - * set 4 -> { 7 15 } - * - * interval = 4 - * deep = 0 - * set 1 -> { 2 6 10 14 } - * deep = 1 - * set 1 -> { 1 5 9 13 } - * set 2 -> { 3 7 11 15 } - * - * interval = 2 - * deep = 0 - * set 1 -> { 1 3 5 7 9 11 13 15 } - */ -static int uwb_rsv_find_best_column_set(struct uwb_rsv_alloc_info *ai, int interval, - int num_safe_mas, int num_unsafe_mas) -{ - struct uwb_rsv_col_info *ci = ai->ci; - struct uwb_rsv_col_set_info *csi = &ci->csi; - struct uwb_rsv_col_set_info tmp_csi; - int deep, set, col, start_col_deep, col_start_set; - int start_col, max_mas_in_set, lowest_max_mas_in_deep; - int n_mas; - int found = UWB_RSV_ALLOC_NOT_FOUND; - - tmp_csi.start_col = 0; - start_col_deep = interval; - n_mas = num_unsafe_mas + num_safe_mas; - - for (deep = 0; ((interval >> deep) & 0x1) == 0; deep++) { - start_col_deep /= 2; - col_start_set = 0; - lowest_max_mas_in_deep = UWB_MAS_PER_ZONE; - - for (set = 1; set <= (1 << deep); set++) { - max_mas_in_set = 0; - start_col = start_col_deep + col_start_set; - for (col = start_col; col < UWB_NUM_ZONES; col += interval) { - - if (ci[col].max_avail_safe >= num_safe_mas && - ci[col].max_avail_unsafe >= n_mas) { - if (ci[col].highest_mas[n_mas] > max_mas_in_set) - max_mas_in_set = ci[col].highest_mas[n_mas]; - } else { - max_mas_in_set = 0; - break; - } - } - if ((lowest_max_mas_in_deep > max_mas_in_set) && max_mas_in_set) { - lowest_max_mas_in_deep = max_mas_in_set; - - tmp_csi.start_col = start_col; - } - col_start_set += (interval >> deep); - } - - if (lowest_max_mas_in_deep < 8) { - csi->start_col = tmp_csi.start_col; - found = UWB_RSV_ALLOC_FOUND; - break; - } else if ((lowest_max_mas_in_deep > 8) && - (lowest_max_mas_in_deep != UWB_MAS_PER_ZONE) && - (found == UWB_RSV_ALLOC_NOT_FOUND)) { - csi->start_col = tmp_csi.start_col; - found = UWB_RSV_ALLOC_FOUND; - } - } - - if (found == UWB_RSV_ALLOC_FOUND) { - csi->interval = interval; - csi->safe_mas_per_col = num_safe_mas; - csi->unsafe_mas_per_col = num_unsafe_mas; - - ai->safe_allocated_mases = (UWB_NUM_ZONES / interval) * num_safe_mas; - ai->unsafe_allocated_mases = (UWB_NUM_ZONES / interval) * num_unsafe_mas; - ai->total_allocated_mases = ai->safe_allocated_mases + ai->unsafe_allocated_mases; - ai->interval = interval; - } - return found; -} - -static void get_row_descriptors(struct uwb_rsv_alloc_info *ai) -{ - unsigned char *bm = ai->bm; - struct uwb_rsv_row_info *ri = &ai->ri; - int col, mas; - - ri->free_rows = 16; - for (mas = 0; mas < UWB_MAS_PER_ZONE; mas ++) { - ri->avail[mas] = 1; - for (col = 1; col < UWB_NUM_ZONES; col++) { - if (bm[col * UWB_NUM_ZONES + mas] == UWB_RSV_MAS_NOT_AVAIL) { - ri->free_rows--; - ri->avail[mas]=0; - break; - } - } - } -} - -static void uwb_rsv_fill_column_info(unsigned char *bm, int column, struct uwb_rsv_col_info *rci) -{ - int mas; - int block_count = 0, start_block = 0; - int previous_avail = 0; - int available = 0; - int safe_mas_in_row[UWB_MAS_PER_ZONE] = { - 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, - }; - - rci->max_avail_safe = 0; - - for (mas = 0; mas < UWB_MAS_PER_ZONE; mas ++) { - if (!bm[column * UWB_NUM_ZONES + mas]) { - available++; - rci->max_avail_unsafe = available; - - rci->highest_mas[available] = mas; - - if (previous_avail) { - block_count++; - if ((block_count > safe_mas_in_row[start_block]) && - (!rci->max_avail_safe)) - rci->max_avail_safe = available - 1; - } else { - previous_avail = 1; - start_block = mas; - block_count = 1; - } - } else { - previous_avail = 0; - } - } - if (!rci->max_avail_safe) - rci->max_avail_safe = rci->max_avail_unsafe; -} - -static void get_column_descriptors(struct uwb_rsv_alloc_info *ai) -{ - unsigned char *bm = ai->bm; - struct uwb_rsv_col_info *ci = ai->ci; - int col; - - for (col = 1; col < UWB_NUM_ZONES; col++) { - uwb_rsv_fill_column_info(bm, col, &ci[col]); - } -} - -static int uwb_rsv_find_best_row_alloc(struct uwb_rsv_alloc_info *ai) -{ - int n_rows; - int max_rows = ai->max_mas / UWB_USABLE_MAS_PER_ROW; - int min_rows = ai->min_mas / UWB_USABLE_MAS_PER_ROW; - if (ai->min_mas % UWB_USABLE_MAS_PER_ROW) - min_rows++; - for (n_rows = max_rows; n_rows >= min_rows; n_rows--) { - if (n_rows <= ai->ri.free_rows) { - ai->ri.used_rows = n_rows; - ai->interval = 1; /* row reservation */ - uwb_rsv_fill_row_alloc(ai); - return UWB_RSV_ALLOC_FOUND; - } - } - return UWB_RSV_ALLOC_NOT_FOUND; -} - -static int uwb_rsv_find_best_col_alloc(struct uwb_rsv_alloc_info *ai, int interval) -{ - int n_safe, n_unsafe, n_mas; - int n_column = UWB_NUM_ZONES / interval; - int max_per_zone = ai->max_mas / n_column; - int min_per_zone = ai->min_mas / n_column; - - if (ai->min_mas % n_column) - min_per_zone++; - - if (min_per_zone > UWB_MAS_PER_ZONE) { - return UWB_RSV_ALLOC_NOT_FOUND; - } - - if (max_per_zone > UWB_MAS_PER_ZONE) { - max_per_zone = UWB_MAS_PER_ZONE; - } - - for (n_mas = max_per_zone; n_mas >= min_per_zone; n_mas--) { - if (uwb_rsv_find_best_column_set(ai, interval, 0, n_mas) == UWB_RSV_ALLOC_NOT_FOUND) - continue; - for (n_safe = n_mas; n_safe >= 0; n_safe--) { - n_unsafe = n_mas - n_safe; - if (uwb_rsv_find_best_column_set(ai, interval, n_safe, n_unsafe) == UWB_RSV_ALLOC_FOUND) { - uwb_rsv_fill_column_alloc(ai); - return UWB_RSV_ALLOC_FOUND; - } - } - } - return UWB_RSV_ALLOC_NOT_FOUND; -} - -int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, struct uwb_mas_bm *available, - struct uwb_mas_bm *result) -{ - struct uwb_rsv_alloc_info *ai; - int interval; - int bit_index; - - ai = kzalloc(sizeof(struct uwb_rsv_alloc_info), GFP_KERNEL); - if (!ai) - return UWB_RSV_ALLOC_NOT_FOUND; - ai->min_mas = rsv->min_mas; - ai->max_mas = rsv->max_mas; - ai->max_interval = rsv->max_interval; - - - /* fill the not available vector from the available bm */ - for_each_clear_bit(bit_index, available->bm, UWB_NUM_MAS) - ai->bm[bit_index] = UWB_RSV_MAS_NOT_AVAIL; - - if (ai->max_interval == 1) { - get_row_descriptors(ai); - if (uwb_rsv_find_best_row_alloc(ai) == UWB_RSV_ALLOC_FOUND) - goto alloc_found; - else - goto alloc_not_found; - } - - get_column_descriptors(ai); - - for (interval = 16; interval >= 2; interval>>=1) { - if (interval > ai->max_interval) - continue; - if (uwb_rsv_find_best_col_alloc(ai, interval) == UWB_RSV_ALLOC_FOUND) - goto alloc_found; - } - - /* try row reservation if no column is found */ - get_row_descriptors(ai); - if (uwb_rsv_find_best_row_alloc(ai) == UWB_RSV_ALLOC_FOUND) - goto alloc_found; - else - goto alloc_not_found; - - alloc_found: - bitmap_zero(result->bm, UWB_NUM_MAS); - bitmap_zero(result->unsafe_bm, UWB_NUM_MAS); - /* fill the safe and unsafe bitmaps */ - for (bit_index = 0; bit_index < UWB_NUM_MAS; bit_index++) { - if (ai->bm[bit_index] == UWB_RSV_MAS_SAFE) - set_bit(bit_index, result->bm); - else if (ai->bm[bit_index] == UWB_RSV_MAS_UNSAFE) - set_bit(bit_index, result->unsafe_bm); - } - bitmap_or(result->bm, result->bm, result->unsafe_bm, UWB_NUM_MAS); - - result->safe = ai->safe_allocated_mases; - result->unsafe = ai->unsafe_allocated_mases; - - kfree(ai); - return UWB_RSV_ALLOC_FOUND; - - alloc_not_found: - kfree(ai); - return UWB_RSV_ALLOC_NOT_FOUND; -} diff --git a/drivers/staging/uwb/beacon.c b/drivers/staging/uwb/beacon.c deleted file mode 100644 index c483c19c5ef8..000000000000 --- a/drivers/staging/uwb/beacon.c +++ /dev/null @@ -1,595 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Beacon management - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - */ -#include -#include -#include -#include -#include -#include -#include - -#include "uwb-internal.h" - -/* Start Beaconing command structure */ -struct uwb_rc_cmd_start_beacon { - struct uwb_rccb rccb; - __le16 wBPSTOffset; - u8 bChannelNumber; -} __attribute__((packed)); - - -static int uwb_rc_start_beacon(struct uwb_rc *rc, u16 bpst_offset, u8 channel) -{ - int result; - struct uwb_rc_cmd_start_beacon *cmd; - struct uwb_rc_evt_confirm reply; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - cmd->rccb.bCommandType = UWB_RC_CET_GENERAL; - cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_START_BEACON); - cmd->wBPSTOffset = cpu_to_le16(bpst_offset); - cmd->bChannelNumber = channel; - reply.rceb.bEventType = UWB_RC_CET_GENERAL; - reply.rceb.wEvent = UWB_RC_CMD_START_BEACON; - result = uwb_rc_cmd(rc, "START-BEACON", &cmd->rccb, sizeof(*cmd), - &reply.rceb, sizeof(reply)); - if (result < 0) - goto error_cmd; - if (reply.bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(&rc->uwb_dev.dev, - "START-BEACON: command execution failed: %s (%d)\n", - uwb_rc_strerror(reply.bResultCode), reply.bResultCode); - result = -EIO; - } -error_cmd: - kfree(cmd); - return result; -} - -static int uwb_rc_stop_beacon(struct uwb_rc *rc) -{ - int result; - struct uwb_rccb *cmd; - struct uwb_rc_evt_confirm reply; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - cmd->bCommandType = UWB_RC_CET_GENERAL; - cmd->wCommand = cpu_to_le16(UWB_RC_CMD_STOP_BEACON); - reply.rceb.bEventType = UWB_RC_CET_GENERAL; - reply.rceb.wEvent = UWB_RC_CMD_STOP_BEACON; - result = uwb_rc_cmd(rc, "STOP-BEACON", cmd, sizeof(*cmd), - &reply.rceb, sizeof(reply)); - if (result < 0) - goto error_cmd; - if (reply.bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(&rc->uwb_dev.dev, - "STOP-BEACON: command execution failed: %s (%d)\n", - uwb_rc_strerror(reply.bResultCode), reply.bResultCode); - result = -EIO; - } -error_cmd: - kfree(cmd); - return result; -} - -/* - * Start/stop beacons - * - * @rc: UWB Radio Controller to operate on - * @channel: UWB channel on which to beacon (WUSB[table - * 5-12]). If -1, stop beaconing. - * @bpst_offset: Beacon Period Start Time offset; FIXME-do zero - * - * According to WHCI 0.95 [4.13.6] the driver will only receive the RCEB - * of a SET IE command after the device sent the first beacon that includes - * the IEs specified in the SET IE command. So, after we start beaconing we - * check if there is anything in the IE cache and call the SET IE command - * if needed. - */ -int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset) -{ - int result; - struct device *dev = &rc->uwb_dev.dev; - - dev_dbg(dev, "%s: channel = %d\n", __func__, channel); - if (channel < 0) - channel = -1; - if (channel == -1) - result = uwb_rc_stop_beacon(rc); - else { - /* channel >= 0...dah */ - result = uwb_rc_start_beacon(rc, bpst_offset, channel); - if (result < 0) { - dev_err(dev, "Cannot start beaconing: %d\n", result); - return result; - } - if (le16_to_cpu(rc->ies->wIELength) > 0) { - result = uwb_rc_set_ie(rc, rc->ies); - if (result < 0) { - dev_err(dev, "Cannot set new IE on device: " - "%d\n", result); - result = uwb_rc_stop_beacon(rc); - channel = -1; - bpst_offset = 0; - } - } - } - - if (result >= 0) - rc->beaconing = channel; - return result; -} - -/* - * Beacon cache - * - * The purpose of this is to speed up the lookup of becon information - * when a new beacon arrives. The UWB Daemon uses it also to keep a - * tab of which devices are in radio distance and which not. When a - * device's beacon stays present for more than a certain amount of - * time, it is considered a new, usable device. When a beacon ceases - * to be received for a certain amount of time, it is considered that - * the device is gone. - * - * FIXME: use an allocator for the entries - * FIXME: use something faster for search than a list - */ - -void uwb_bce_kfree(struct kref *_bce) -{ - struct uwb_beca_e *bce = container_of(_bce, struct uwb_beca_e, refcnt); - - kfree(bce->be); - kfree(bce); -} - - -/* Find a beacon by dev addr in the cache */ -static -struct uwb_beca_e *__uwb_beca_find_bydev(struct uwb_rc *rc, - const struct uwb_dev_addr *dev_addr) -{ - struct uwb_beca_e *bce, *next; - list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) { - if (!memcmp(&bce->dev_addr, dev_addr, sizeof(bce->dev_addr))) - goto out; - } - bce = NULL; -out: - return bce; -} - -/* Find a beacon by dev addr in the cache */ -static -struct uwb_beca_e *__uwb_beca_find_bymac(struct uwb_rc *rc, - const struct uwb_mac_addr *mac_addr) -{ - struct uwb_beca_e *bce, *next; - list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) { - if (!memcmp(bce->mac_addr, mac_addr->data, - sizeof(struct uwb_mac_addr))) - goto out; - } - bce = NULL; -out: - return bce; -} - -/** - * uwb_dev_get_by_devaddr - get a UWB device with a specific DevAddr - * @rc: the radio controller that saw the device - * @devaddr: DevAddr of the UWB device to find - * - * There may be more than one matching device (in the case of a - * DevAddr conflict), but only the first one is returned. - */ -struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc, - const struct uwb_dev_addr *devaddr) -{ - struct uwb_dev *found = NULL; - struct uwb_beca_e *bce; - - mutex_lock(&rc->uwb_beca.mutex); - bce = __uwb_beca_find_bydev(rc, devaddr); - if (bce) - found = uwb_dev_try_get(rc, bce->uwb_dev); - mutex_unlock(&rc->uwb_beca.mutex); - - return found; -} - -/** - * uwb_dev_get_by_macaddr - get a UWB device with a specific EUI-48 - * @rc: the radio controller that saw the device - * @devaddr: EUI-48 of the UWB device to find - */ -struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc, - const struct uwb_mac_addr *macaddr) -{ - struct uwb_dev *found = NULL; - struct uwb_beca_e *bce; - - mutex_lock(&rc->uwb_beca.mutex); - bce = __uwb_beca_find_bymac(rc, macaddr); - if (bce) - found = uwb_dev_try_get(rc, bce->uwb_dev); - mutex_unlock(&rc->uwb_beca.mutex); - - return found; -} - -/* Initialize a beacon cache entry */ -static void uwb_beca_e_init(struct uwb_beca_e *bce) -{ - mutex_init(&bce->mutex); - kref_init(&bce->refcnt); - stats_init(&bce->lqe_stats); - stats_init(&bce->rssi_stats); -} - -/* - * Add a beacon to the cache - * - * @be: Beacon event information - * @bf: Beacon frame (part of b, really) - * @ts_jiffies: Timestamp (in jiffies) when the beacon was received - */ -static -struct uwb_beca_e *__uwb_beca_add(struct uwb_rc *rc, - struct uwb_rc_evt_beacon *be, - struct uwb_beacon_frame *bf, - unsigned long ts_jiffies) -{ - struct uwb_beca_e *bce; - - bce = kzalloc(sizeof(*bce), GFP_KERNEL); - if (bce == NULL) - return NULL; - uwb_beca_e_init(bce); - bce->ts_jiffies = ts_jiffies; - bce->uwb_dev = NULL; - list_add(&bce->node, &rc->uwb_beca.list); - return bce; -} - -/* - * Wipe out beacon entries that became stale - * - * Remove associated devicest too. - */ -void uwb_beca_purge(struct uwb_rc *rc) -{ - struct uwb_beca_e *bce, *next; - unsigned long expires; - - mutex_lock(&rc->uwb_beca.mutex); - list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) { - expires = bce->ts_jiffies + msecs_to_jiffies(beacon_timeout_ms); - if (time_after(jiffies, expires)) { - uwbd_dev_offair(bce); - } - } - mutex_unlock(&rc->uwb_beca.mutex); -} - -/* Clean up the whole beacon cache. Called on shutdown */ -void uwb_beca_release(struct uwb_rc *rc) -{ - struct uwb_beca_e *bce, *next; - - mutex_lock(&rc->uwb_beca.mutex); - list_for_each_entry_safe(bce, next, &rc->uwb_beca.list, node) { - list_del(&bce->node); - uwb_bce_put(bce); - } - mutex_unlock(&rc->uwb_beca.mutex); -} - -static void uwb_beacon_print(struct uwb_rc *rc, struct uwb_rc_evt_beacon *be, - struct uwb_beacon_frame *bf) -{ - char macbuf[UWB_ADDR_STRSIZE]; - char devbuf[UWB_ADDR_STRSIZE]; - char dstbuf[UWB_ADDR_STRSIZE]; - - uwb_mac_addr_print(macbuf, sizeof(macbuf), &bf->Device_Identifier); - uwb_dev_addr_print(devbuf, sizeof(devbuf), &bf->hdr.SrcAddr); - uwb_dev_addr_print(dstbuf, sizeof(dstbuf), &bf->hdr.DestAddr); - dev_info(&rc->uwb_dev.dev, - "BEACON from %s to %s (ch%u offset %u slot %u MAC %s)\n", - devbuf, dstbuf, be->bChannelNumber, be->wBPSTOffset, - bf->Beacon_Slot_Number, macbuf); -} - -/* - * @bce: beacon cache entry, referenced - */ -ssize_t uwb_bce_print_IEs(struct uwb_dev *uwb_dev, struct uwb_beca_e *bce, - char *buf, size_t size) -{ - ssize_t result = 0; - struct uwb_rc_evt_beacon *be; - struct uwb_beacon_frame *bf; - int ies_len; - struct uwb_ie_hdr *ies; - - mutex_lock(&bce->mutex); - - be = bce->be; - if (be) { - bf = (struct uwb_beacon_frame *)bce->be->BeaconInfo; - ies_len = be->wBeaconInfoLength - sizeof(struct uwb_beacon_frame); - ies = (struct uwb_ie_hdr *)bf->IEData; - - result = uwb_ie_dump_hex(ies, ies_len, buf, size); - } - - mutex_unlock(&bce->mutex); - - return result; -} - -/* - * Verify that the beacon event, frame and IEs are ok - */ -static int uwb_verify_beacon(struct uwb_rc *rc, struct uwb_event *evt, - struct uwb_rc_evt_beacon *be) -{ - int result = -EINVAL; - struct uwb_beacon_frame *bf; - struct device *dev = &rc->uwb_dev.dev; - - /* Is there enough data to decode a beacon frame? */ - if (evt->notif.size < sizeof(*be) + sizeof(*bf)) { - dev_err(dev, "BEACON event: Not enough data to decode " - "(%zu vs %zu bytes needed)\n", evt->notif.size, - sizeof(*be) + sizeof(*bf)); - goto error; - } - /* FIXME: make sure beacon frame IEs are fine and that the whole thing - * is consistent */ - result = 0; -error: - return result; -} - -/* - * Handle UWB_RC_EVT_BEACON events - * - * We check the beacon cache to see how the received beacon fares. If - * is there already we refresh the timestamp. If not we create a new - * entry. - * - * According to the WHCI and WUSB specs, only one beacon frame is - * allowed per notification block, so we don't bother about scanning - * for more. - */ -int uwbd_evt_handle_rc_beacon(struct uwb_event *evt) -{ - int result = -EINVAL; - struct uwb_rc *rc; - struct uwb_rc_evt_beacon *be; - struct uwb_beacon_frame *bf; - struct uwb_beca_e *bce; - - rc = evt->rc; - be = container_of(evt->notif.rceb, struct uwb_rc_evt_beacon, rceb); - result = uwb_verify_beacon(rc, evt, be); - if (result < 0) - return result; - - /* FIXME: handle alien beacons. */ - if (be->bBeaconType == UWB_RC_BEACON_TYPE_OL_ALIEN || - be->bBeaconType == UWB_RC_BEACON_TYPE_NOL_ALIEN) { - return -ENOSYS; - } - - bf = (struct uwb_beacon_frame *) be->BeaconInfo; - - /* - * Drop beacons from devices with a NULL EUI-48 -- they cannot - * be uniquely identified. - * - * It's expected that these will all be WUSB devices and they - * have a WUSB specific connection method so ignoring them - * here shouldn't be a problem. - */ - if (uwb_mac_addr_bcast(&bf->Device_Identifier)) - return 0; - - mutex_lock(&rc->uwb_beca.mutex); - bce = __uwb_beca_find_bymac(rc, &bf->Device_Identifier); - if (bce == NULL) { - /* Not in there, a new device is pinging */ - uwb_beacon_print(evt->rc, be, bf); - bce = __uwb_beca_add(rc, be, bf, evt->ts_jiffies); - if (bce == NULL) { - mutex_unlock(&rc->uwb_beca.mutex); - return -ENOMEM; - } - } - mutex_unlock(&rc->uwb_beca.mutex); - - mutex_lock(&bce->mutex); - /* purge old beacon data */ - kfree(bce->be); - - /* Update commonly used fields */ - bce->ts_jiffies = evt->ts_jiffies; - bce->be = be; - bce->dev_addr = bf->hdr.SrcAddr; - bce->mac_addr = &bf->Device_Identifier; - be->wBPSTOffset = le16_to_cpu(be->wBPSTOffset); - be->wBeaconInfoLength = le16_to_cpu(be->wBeaconInfoLength); - stats_add_sample(&bce->lqe_stats, be->bLQI - 7); - stats_add_sample(&bce->rssi_stats, be->bRSSI + 18); - - /* - * This might be a beacon from a new device. - */ - if (bce->uwb_dev == NULL) - uwbd_dev_onair(evt->rc, bce); - - mutex_unlock(&bce->mutex); - - return 1; /* we keep the event data */ -} - -/* - * Handle UWB_RC_EVT_BEACON_SIZE events - * - * XXXXX - */ -int uwbd_evt_handle_rc_beacon_size(struct uwb_event *evt) -{ - int result = -EINVAL; - struct device *dev = &evt->rc->uwb_dev.dev; - struct uwb_rc_evt_beacon_size *bs; - - /* Is there enough data to decode the event? */ - if (evt->notif.size < sizeof(*bs)) { - dev_err(dev, "BEACON SIZE notification: Not enough data to " - "decode (%zu vs %zu bytes needed)\n", - evt->notif.size, sizeof(*bs)); - goto error; - } - bs = container_of(evt->notif.rceb, struct uwb_rc_evt_beacon_size, rceb); - if (0) - dev_info(dev, "Beacon size changed to %u bytes " - "(FIXME: action?)\n", le16_to_cpu(bs->wNewBeaconSize)); - else { - /* temporary hack until we do something with this message... */ - static unsigned count; - if (++count % 1000 == 0) - dev_info(dev, "Beacon size changed %u times " - "(FIXME: action?)\n", count); - } - result = 0; -error: - return result; -} - -/** - * uwbd_evt_handle_rc_bp_slot_change - handle a BP_SLOT_CHANGE event - * @evt: the BP_SLOT_CHANGE notification from the radio controller - * - * If the event indicates that no beacon period slots were available - * then radio controller has transitioned to a non-beaconing state. - * Otherwise, simply save the current beacon slot. - */ -int uwbd_evt_handle_rc_bp_slot_change(struct uwb_event *evt) -{ - struct uwb_rc *rc = evt->rc; - struct device *dev = &rc->uwb_dev.dev; - struct uwb_rc_evt_bp_slot_change *bpsc; - - if (evt->notif.size < sizeof(*bpsc)) { - dev_err(dev, "BP SLOT CHANGE event: Not enough data\n"); - return -EINVAL; - } - bpsc = container_of(evt->notif.rceb, struct uwb_rc_evt_bp_slot_change, rceb); - - if (uwb_rc_evt_bp_slot_change_no_slot(bpsc)) { - dev_err(dev, "stopped beaconing: No free slots in BP\n"); - mutex_lock(&rc->uwb_dev.mutex); - rc->beaconing = -1; - mutex_unlock(&rc->uwb_dev.mutex); - } else - rc->uwb_dev.beacon_slot = uwb_rc_evt_bp_slot_change_slot_num(bpsc); - - return 0; -} - -/** - * Handle UWB_RC_EVT_BPOIE_CHANGE events - * - * XXXXX - */ -struct uwb_ie_bpo { - struct uwb_ie_hdr hdr; - u8 bp_length; - u8 data[]; -} __attribute__((packed)); - -int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *evt) -{ - int result = -EINVAL; - struct device *dev = &evt->rc->uwb_dev.dev; - struct uwb_rc_evt_bpoie_change *bpoiec; - struct uwb_ie_bpo *bpoie; - static unsigned count; /* FIXME: this is a temp hack */ - size_t iesize; - - /* Is there enough data to decode it? */ - if (evt->notif.size < sizeof(*bpoiec)) { - dev_err(dev, "BPOIEC notification: Not enough data to " - "decode (%zu vs %zu bytes needed)\n", - evt->notif.size, sizeof(*bpoiec)); - goto error; - } - bpoiec = container_of(evt->notif.rceb, struct uwb_rc_evt_bpoie_change, rceb); - iesize = le16_to_cpu(bpoiec->wBPOIELength); - if (iesize < sizeof(*bpoie)) { - dev_err(dev, "BPOIEC notification: Not enough IE data to " - "decode (%zu vs %zu bytes needed)\n", - iesize, sizeof(*bpoie)); - goto error; - } - if (++count % 1000 == 0) /* Lame placeholder */ - dev_info(dev, "BPOIE: %u changes received\n", count); - /* - * FIXME: At this point we should go over all the IEs in the - * bpoiec->BPOIE array and act on each. - */ - result = 0; -error: - return result; -} - -/* - * Print beaconing state. - */ -static ssize_t uwb_rc_beacon_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_rc *rc = uwb_dev->rc; - ssize_t result; - - mutex_lock(&rc->uwb_dev.mutex); - result = sprintf(buf, "%d\n", rc->beaconing); - mutex_unlock(&rc->uwb_dev.mutex); - return result; -} - -/* - * Start beaconing on the specified channel, or stop beaconing. - */ -static ssize_t uwb_rc_beacon_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_rc *rc = uwb_dev->rc; - int channel; - ssize_t result = -EINVAL; - - result = sscanf(buf, "%d", &channel); - if (result >= 1) - result = uwb_radio_force_channel(rc, channel); - - return result < 0 ? result : size; -} -DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, uwb_rc_beacon_show, uwb_rc_beacon_store); diff --git a/drivers/staging/uwb/driver.c b/drivers/staging/uwb/driver.c deleted file mode 100644 index 5755c2e49ffc..000000000000 --- a/drivers/staging/uwb/driver.c +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Driver initialization, etc - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - * - * Life cycle: FIXME: explain - * - * UWB radio controller: - * - * 1. alloc a uwb_rc, zero it - * 2. call uwb_rc_init() on it to set it up + ops (won't do any - * kind of allocation) - * 3. register (now it is owned by the UWB stack--deregister before - * freeing/destroying). - * 4. It lives on it's own now (UWB stack handles)--when it - * disconnects, call unregister() - * 5. free it. - * - * Make sure you have a reference to the uwb_rc before calling - * any of the UWB API functions. - * - * TODO: - * - * 1. Locking and life cycle management is crappy still. All entry - * points to the UWB HCD API assume you have a reference on the - * uwb_rc structure and that it won't go away. They mutex lock it - * before doing anything. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "uwb-internal.h" - - -/* UWB stack attributes (or 'global' constants) */ - - -/** - * If a beacon disappears for longer than this, then we consider the - * device who was represented by that beacon to be gone. - * - * ECMA-368[17.2.3, last para] establishes that a device must not - * consider a device to be its neighbour if he doesn't receive a beacon - * for more than mMaxLostBeacons. mMaxLostBeacons is defined in - * ECMA-368[17.16] as 3; because we can get only one beacon per - * superframe, that'd be 3 * 65ms = 195 ~ 200 ms. Let's give it time - * for jitter and stuff and make it 500 ms. - */ -unsigned long beacon_timeout_ms = 500; - -static -ssize_t beacon_timeout_ms_show(struct class *class, - struct class_attribute *attr, - char *buf) -{ - return scnprintf(buf, PAGE_SIZE, "%lu\n", beacon_timeout_ms); -} - -static -ssize_t beacon_timeout_ms_store(struct class *class, - struct class_attribute *attr, - const char *buf, size_t size) -{ - unsigned long bt; - ssize_t result; - result = sscanf(buf, "%lu", &bt); - if (result != 1) - return -EINVAL; - beacon_timeout_ms = bt; - return size; -} -static CLASS_ATTR_RW(beacon_timeout_ms); - -static struct attribute *uwb_class_attrs[] = { - &class_attr_beacon_timeout_ms.attr, - NULL, -}; -ATTRIBUTE_GROUPS(uwb_class); - -/** Device model classes */ -struct class uwb_rc_class = { - .name = "uwb_rc", - .class_groups = uwb_class_groups, -}; - - -static int __init uwb_subsys_init(void) -{ - int result = 0; - - result = uwb_est_create(); - if (result < 0) { - printk(KERN_ERR "uwb: Can't initialize EST subsystem\n"); - goto error_est_init; - } - - result = class_register(&uwb_rc_class); - if (result < 0) - goto error_uwb_rc_class_register; - - /* Register the UWB bus */ - result = bus_register(&uwb_bus_type); - if (result) { - pr_err("%s - registering bus driver failed\n", __func__); - goto exit_bus; - } - - uwb_dbg_init(); - return 0; - -exit_bus: - class_unregister(&uwb_rc_class); -error_uwb_rc_class_register: - uwb_est_destroy(); -error_est_init: - return result; -} -module_init(uwb_subsys_init); - -static void __exit uwb_subsys_exit(void) -{ - uwb_dbg_exit(); - bus_unregister(&uwb_bus_type); - class_unregister(&uwb_rc_class); - uwb_est_destroy(); - return; -} -module_exit(uwb_subsys_exit); - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("Ultra Wide Band core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/uwb/drp-avail.c b/drivers/staging/uwb/drp-avail.c deleted file mode 100644 index 02392ab82a7d..000000000000 --- a/drivers/staging/uwb/drp-avail.c +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * DRP availability management - * - * Copyright (C) 2005-2006 Intel Corporation - * Reinette Chatre - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - * - * Manage DRP Availability (the MAS available for DRP - * reservations). Thus: - * - * - Handle DRP Availability Change notifications - * - * - Allow the reservation manager to indicate MAS reserved/released - * by local (owned by/targeted at the radio controller) - * reservations. - * - * - Based on the two sources above, generate a DRP Availability IE to - * be included in the beacon. - * - * See also the documentation for struct uwb_drp_avail. - */ - -#include -#include -#include -#include -#include "uwb-internal.h" - -/** - * uwb_drp_avail_init - initialize an RC's MAS availability - * - * All MAS are available initially. The RC will inform use which - * slots are used for the BP (it may change in size). - */ -void uwb_drp_avail_init(struct uwb_rc *rc) -{ - bitmap_fill(rc->drp_avail.global, UWB_NUM_MAS); - bitmap_fill(rc->drp_avail.local, UWB_NUM_MAS); - bitmap_fill(rc->drp_avail.pending, UWB_NUM_MAS); -} - -/* - * Determine MAS available for new local reservations. - * - * avail = global & local & pending - */ -void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail) -{ - bitmap_and(avail->bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS); - bitmap_and(avail->bm, avail->bm, rc->drp_avail.pending, UWB_NUM_MAS); -} - -/** - * uwb_drp_avail_reserve_pending - reserve MAS for a new reservation - * @rc: the radio controller - * @mas: the MAS to reserve - * - * Returns 0 on success, or -EBUSY if the MAS requested aren't available. - */ -int uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas) -{ - struct uwb_mas_bm avail; - - uwb_drp_available(rc, &avail); - if (!bitmap_subset(mas->bm, avail.bm, UWB_NUM_MAS)) - return -EBUSY; - - bitmap_andnot(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS); - return 0; -} - -/** - * uwb_drp_avail_reserve - reserve MAS for an established reservation - * @rc: the radio controller - * @mas: the MAS to reserve - */ -void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas) -{ - bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS); - bitmap_andnot(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS); - rc->drp_avail.ie_valid = false; -} - -/** - * uwb_drp_avail_release - release MAS from a pending or established reservation - * @rc: the radio controller - * @mas: the MAS to release - */ -void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas) -{ - bitmap_or(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS); - bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS); - rc->drp_avail.ie_valid = false; - uwb_rsv_handle_drp_avail_change(rc); -} - -/** - * uwb_drp_avail_ie_update - update the DRP Availability IE - * @rc: the radio controller - * - * avail = global & local - */ -void uwb_drp_avail_ie_update(struct uwb_rc *rc) -{ - struct uwb_mas_bm avail; - - bitmap_and(avail.bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS); - - rc->drp_avail.ie.hdr.element_id = UWB_IE_DRP_AVAILABILITY; - rc->drp_avail.ie.hdr.length = UWB_NUM_MAS / 8; - uwb_mas_bm_copy_le(rc->drp_avail.ie.bmp, &avail); - rc->drp_avail.ie_valid = true; -} - -/** - * Create an unsigned long from a buffer containing a byte stream. - * - * @array: pointer to buffer - * @itr: index of buffer from where we start - * @len: the buffer's remaining size may not be exact multiple of - * sizeof(unsigned long), @len is the length of buffer that needs - * to be converted. This will be sizeof(unsigned long) or smaller - * (BUG if not). If it is smaller then we will pad the remaining - * space of the result with zeroes. - */ -static -unsigned long get_val(u8 *array, size_t itr, size_t len) -{ - unsigned long val = 0; - size_t top = itr + len; - - BUG_ON(len > sizeof(val)); - - while (itr < top) { - val <<= 8; - val |= array[top - 1]; - top--; - } - val <<= 8 * (sizeof(val) - len); /* padding */ - return val; -} - -/** - * Initialize bitmap from data buffer. - * - * The bitmap to be converted could come from a IE, for example a - * DRP Availability IE. - * From ECMA-368 1.0 [16.8.7]: " - * octets: 1 1 N * (0 to 32) - * Element ID Length (=N) DRP Availability Bitmap - * - * The DRP Availability Bitmap field is up to 256 bits long, one - * bit for each MAS in the superframe, where the least-significant - * bit of the field corresponds to the first MAS in the superframe - * and successive bits correspond to successive MASs." - * - * The DRP Availability bitmap is in octets from 0 to 32, so octet - * 32 contains bits for MAS 1-8, etc. If the bitmap is smaller than 32 - * octets, the bits in octets not included at the end of the bitmap are - * treated as zero. In this case (when the bitmap is smaller than 32 - * octets) the MAS represented range from MAS 1 to MAS (size of bitmap) - * with the last octet still containing bits for MAS 1-8, etc. - * - * For example: - * F00F0102 03040506 0708090A 0B0C0D0E 0F010203 - * ^^^^ - * |||| - * |||| - * |||\LSB of byte is MAS 9 - * ||\MSB of byte is MAS 16 - * |\LSB of first byte is MAS 1 - * \ MSB of byte is MAS 8 - * - * An example of this encoding can be found in ECMA-368 Annex-D [Table D.11] - * - * The resulting bitmap will have the following mapping: - * bit position 0 == MAS 1 - * bit position 1 == MAS 2 - * ... - * bit position (UWB_NUM_MAS - 1) == MAS UWB_NUM_MAS - * - * @bmp_itr: pointer to bitmap (can be declared with DECLARE_BITMAP) - * @buffer: pointer to buffer containing bitmap data in big endian - * format (MSB first) - * @buffer_size:number of bytes with which bitmap should be initialized - */ -static -void buffer_to_bmp(unsigned long *bmp_itr, void *_buffer, - size_t buffer_size) -{ - u8 *buffer = _buffer; - size_t itr, len; - unsigned long val; - - itr = 0; - while (itr < buffer_size) { - len = buffer_size - itr >= sizeof(val) ? - sizeof(val) : buffer_size - itr; - val = get_val(buffer, itr, len); - bmp_itr[itr / sizeof(val)] = val; - itr += sizeof(val); - } -} - - -/** - * Extract DRP Availability bitmap from the notification. - * - * The notification that comes in contains a bitmap of (UWB_NUM_MAS / 8) bytes - * We convert that to our internal representation. - */ -static -int uwbd_evt_get_drp_avail(struct uwb_event *evt, unsigned long *bmp) -{ - struct device *dev = &evt->rc->uwb_dev.dev; - struct uwb_rc_evt_drp_avail *drp_evt; - int result = -EINVAL; - - /* Is there enough data to decode the event? */ - if (evt->notif.size < sizeof(*drp_evt)) { - dev_err(dev, "DRP Availability Change: Not enough " - "data to decode event [%zu bytes, %zu " - "needed]\n", evt->notif.size, sizeof(*drp_evt)); - goto error; - } - drp_evt = container_of(evt->notif.rceb, struct uwb_rc_evt_drp_avail, rceb); - buffer_to_bmp(bmp, drp_evt->bmp, UWB_NUM_MAS/8); - result = 0; -error: - return result; -} - - -/** - * Process an incoming DRP Availability notification. - * - * @evt: Event information (packs the actual event data, which - * radio controller it came to, etc). - * - * @returns: 0 on success (so uwbd() frees the event buffer), < 0 - * on error. - * - * According to ECMA-368 1.0 [16.8.7], bits set to ONE indicate that - * the MAS slot is available, bits set to ZERO indicate that the slot - * is busy. - * - * So we clear available slots, we set used slots :) - * - * The notification only marks non-availability based on the BP and - * received DRP IEs that are not for this radio controller. A copy of - * this bitmap is needed to generate the real availability (which - * includes local and pending reservations). - * - * The DRP Availability IE that this radio controller emits will need - * to be updated. - */ -int uwbd_evt_handle_rc_drp_avail(struct uwb_event *evt) -{ - int result; - struct uwb_rc *rc = evt->rc; - DECLARE_BITMAP(bmp, UWB_NUM_MAS); - - result = uwbd_evt_get_drp_avail(evt, bmp); - if (result < 0) - return result; - - mutex_lock(&rc->rsvs_mutex); - bitmap_copy(rc->drp_avail.global, bmp, UWB_NUM_MAS); - rc->drp_avail.ie_valid = false; - uwb_rsv_handle_drp_avail_change(rc); - mutex_unlock(&rc->rsvs_mutex); - - uwb_rsv_sched_update(rc); - - return 0; -} diff --git a/drivers/staging/uwb/drp-ie.c b/drivers/staging/uwb/drp-ie.c deleted file mode 100644 index b2a862cf76de..000000000000 --- a/drivers/staging/uwb/drp-ie.c +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * UWB DRP IE management. - * - * Copyright (C) 2005-2006 Intel Corporation - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include - -#include "uwb.h" -#include "uwb-internal.h" - - -/* - * Return the reason code for a reservations's DRP IE. - */ -static int uwb_rsv_reason_code(struct uwb_rsv *rsv) -{ - static const int reason_codes[] = { - [UWB_RSV_STATE_O_INITIATED] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_O_PENDING] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_O_MODIFIED] = UWB_DRP_REASON_MODIFIED, - [UWB_RSV_STATE_O_ESTABLISHED] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_O_TO_BE_MOVED] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_O_MOVE_COMBINING] = UWB_DRP_REASON_MODIFIED, - [UWB_RSV_STATE_O_MOVE_REDUCING] = UWB_DRP_REASON_MODIFIED, - [UWB_RSV_STATE_O_MOVE_EXPANDING] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_T_ACCEPTED] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_T_CONFLICT] = UWB_DRP_REASON_CONFLICT, - [UWB_RSV_STATE_T_PENDING] = UWB_DRP_REASON_PENDING, - [UWB_RSV_STATE_T_DENIED] = UWB_DRP_REASON_DENIED, - [UWB_RSV_STATE_T_RESIZED] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = UWB_DRP_REASON_CONFLICT, - [UWB_RSV_STATE_T_EXPANDING_PENDING] = UWB_DRP_REASON_PENDING, - [UWB_RSV_STATE_T_EXPANDING_DENIED] = UWB_DRP_REASON_DENIED, - }; - - return reason_codes[rsv->state]; -} - -/* - * Return the reason code for a reservations's companion DRP IE . - */ -static int uwb_rsv_companion_reason_code(struct uwb_rsv *rsv) -{ - static const int companion_reason_codes[] = { - [UWB_RSV_STATE_O_MOVE_EXPANDING] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = UWB_DRP_REASON_ACCEPTED, - [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = UWB_DRP_REASON_CONFLICT, - [UWB_RSV_STATE_T_EXPANDING_PENDING] = UWB_DRP_REASON_PENDING, - [UWB_RSV_STATE_T_EXPANDING_DENIED] = UWB_DRP_REASON_DENIED, - }; - - return companion_reason_codes[rsv->state]; -} - -/* - * Return the status bit for a reservations's DRP IE. - */ -int uwb_rsv_status(struct uwb_rsv *rsv) -{ - static const int statuses[] = { - [UWB_RSV_STATE_O_INITIATED] = 0, - [UWB_RSV_STATE_O_PENDING] = 0, - [UWB_RSV_STATE_O_MODIFIED] = 1, - [UWB_RSV_STATE_O_ESTABLISHED] = 1, - [UWB_RSV_STATE_O_TO_BE_MOVED] = 0, - [UWB_RSV_STATE_O_MOVE_COMBINING] = 1, - [UWB_RSV_STATE_O_MOVE_REDUCING] = 1, - [UWB_RSV_STATE_O_MOVE_EXPANDING] = 1, - [UWB_RSV_STATE_T_ACCEPTED] = 1, - [UWB_RSV_STATE_T_CONFLICT] = 0, - [UWB_RSV_STATE_T_PENDING] = 0, - [UWB_RSV_STATE_T_DENIED] = 0, - [UWB_RSV_STATE_T_RESIZED] = 1, - [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = 1, - [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = 1, - [UWB_RSV_STATE_T_EXPANDING_PENDING] = 1, - [UWB_RSV_STATE_T_EXPANDING_DENIED] = 1, - - }; - - return statuses[rsv->state]; -} - -/* - * Return the status bit for a reservations's companion DRP IE . - */ -int uwb_rsv_companion_status(struct uwb_rsv *rsv) -{ - static const int companion_statuses[] = { - [UWB_RSV_STATE_O_MOVE_EXPANDING] = 0, - [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = 1, - [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = 0, - [UWB_RSV_STATE_T_EXPANDING_PENDING] = 0, - [UWB_RSV_STATE_T_EXPANDING_DENIED] = 0, - }; - - return companion_statuses[rsv->state]; -} - -/* - * Allocate a DRP IE. - * - * To save having to free/allocate a DRP IE when its MAS changes, - * enough memory is allocated for the maxiumum number of DRP - * allocation fields. This gives an overhead per reservation of up to - * (UWB_NUM_ZONES - 1) * 4 = 60 octets. - */ -static struct uwb_ie_drp *uwb_drp_ie_alloc(void) -{ - struct uwb_ie_drp *drp_ie; - - drp_ie = kzalloc(struct_size(drp_ie, allocs, UWB_NUM_ZONES), - GFP_KERNEL); - if (drp_ie) - drp_ie->hdr.element_id = UWB_IE_DRP; - return drp_ie; -} - - -/* - * Fill a DRP IE's allocation fields from a MAS bitmap. - */ -static void uwb_drp_ie_from_bm(struct uwb_ie_drp *drp_ie, - struct uwb_mas_bm *mas) -{ - int z, i, num_fields = 0, next = 0; - struct uwb_drp_alloc *zones; - __le16 current_bmp; - DECLARE_BITMAP(tmp_bmp, UWB_NUM_MAS); - DECLARE_BITMAP(tmp_mas_bm, UWB_MAS_PER_ZONE); - - zones = drp_ie->allocs; - - bitmap_copy(tmp_bmp, mas->bm, UWB_NUM_MAS); - - /* Determine unique MAS bitmaps in zones from bitmap. */ - for (z = 0; z < UWB_NUM_ZONES; z++) { - bitmap_copy(tmp_mas_bm, tmp_bmp, UWB_MAS_PER_ZONE); - if (bitmap_weight(tmp_mas_bm, UWB_MAS_PER_ZONE) > 0) { - bool found = false; - current_bmp = (__le16) *tmp_mas_bm; - for (i = 0; i < next; i++) { - if (current_bmp == zones[i].mas_bm) { - zones[i].zone_bm |= 1 << z; - found = true; - break; - } - } - if (!found) { - num_fields++; - zones[next].zone_bm = 1 << z; - zones[next].mas_bm = current_bmp; - next++; - } - } - bitmap_shift_right(tmp_bmp, tmp_bmp, UWB_MAS_PER_ZONE, UWB_NUM_MAS); - } - - /* Store in format ready for transmission (le16). */ - for (i = 0; i < num_fields; i++) { - drp_ie->allocs[i].zone_bm = cpu_to_le16(zones[i].zone_bm); - drp_ie->allocs[i].mas_bm = cpu_to_le16(zones[i].mas_bm); - } - - drp_ie->hdr.length = sizeof(struct uwb_ie_drp) - sizeof(struct uwb_ie_hdr) - + num_fields * sizeof(struct uwb_drp_alloc); -} - -/** - * uwb_drp_ie_update - update a reservation's DRP IE - * @rsv: the reservation - */ -int uwb_drp_ie_update(struct uwb_rsv *rsv) -{ - struct uwb_ie_drp *drp_ie; - struct uwb_rsv_move *mv; - int unsafe; - - if (rsv->state == UWB_RSV_STATE_NONE) { - kfree(rsv->drp_ie); - rsv->drp_ie = NULL; - return 0; - } - - unsafe = rsv->mas.unsafe ? 1 : 0; - - if (rsv->drp_ie == NULL) { - rsv->drp_ie = uwb_drp_ie_alloc(); - if (rsv->drp_ie == NULL) - return -ENOMEM; - } - drp_ie = rsv->drp_ie; - - uwb_ie_drp_set_unsafe(drp_ie, unsafe); - uwb_ie_drp_set_tiebreaker(drp_ie, rsv->tiebreaker); - uwb_ie_drp_set_owner(drp_ie, uwb_rsv_is_owner(rsv)); - uwb_ie_drp_set_status(drp_ie, uwb_rsv_status(rsv)); - uwb_ie_drp_set_reason_code(drp_ie, uwb_rsv_reason_code(rsv)); - uwb_ie_drp_set_stream_index(drp_ie, rsv->stream); - uwb_ie_drp_set_type(drp_ie, rsv->type); - - if (uwb_rsv_is_owner(rsv)) { - switch (rsv->target.type) { - case UWB_RSV_TARGET_DEV: - drp_ie->dev_addr = rsv->target.dev->dev_addr; - break; - case UWB_RSV_TARGET_DEVADDR: - drp_ie->dev_addr = rsv->target.devaddr; - break; - } - } else - drp_ie->dev_addr = rsv->owner->dev_addr; - - uwb_drp_ie_from_bm(drp_ie, &rsv->mas); - - if (uwb_rsv_has_two_drp_ies(rsv)) { - mv = &rsv->mv; - if (mv->companion_drp_ie == NULL) { - mv->companion_drp_ie = uwb_drp_ie_alloc(); - if (mv->companion_drp_ie == NULL) - return -ENOMEM; - } - drp_ie = mv->companion_drp_ie; - - /* keep all the same configuration of the main drp_ie */ - memcpy(drp_ie, rsv->drp_ie, sizeof(struct uwb_ie_drp)); - - - /* FIXME: handle properly the unsafe bit */ - uwb_ie_drp_set_unsafe(drp_ie, 1); - uwb_ie_drp_set_status(drp_ie, uwb_rsv_companion_status(rsv)); - uwb_ie_drp_set_reason_code(drp_ie, uwb_rsv_companion_reason_code(rsv)); - - uwb_drp_ie_from_bm(drp_ie, &mv->companion_mas); - } - - rsv->ie_valid = true; - return 0; -} - -/* - * Set MAS bits from given MAS bitmap in a single zone of large bitmap. - * - * We are given a zone id and the MAS bitmap of bits that need to be set in - * this zone. Note that this zone may already have bits set and this only - * adds settings - we cannot simply assign the MAS bitmap contents to the - * zone contents. We iterate over the the bits (MAS) in the zone and set the - * bits that are set in the given MAS bitmap. - */ -static -void uwb_drp_ie_single_zone_to_bm(struct uwb_mas_bm *bm, u8 zone, u16 mas_bm) -{ - int mas; - u16 mas_mask; - - for (mas = 0; mas < UWB_MAS_PER_ZONE; mas++) { - mas_mask = 1 << mas; - if (mas_bm & mas_mask) - set_bit(zone * UWB_NUM_ZONES + mas, bm->bm); - } -} - -/** - * uwb_drp_ie_zones_to_bm - convert DRP allocation fields to a bitmap - * @mas: MAS bitmap that will be populated to correspond to the - * allocation fields in the DRP IE - * @drp_ie: the DRP IE that contains the allocation fields. - * - * The input format is an array of MAS allocation fields (16 bit Zone - * bitmap, 16 bit MAS bitmap) as described in [ECMA-368] section - * 16.8.6. The output is a full 256 bit MAS bitmap. - * - * We go over all the allocation fields, for each allocation field we - * know which zones are impacted. We iterate over all the zones - * impacted and call a function that will set the correct MAS bits in - * each zone. - */ -void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie) -{ - int numallocs = (drp_ie->hdr.length - 4) / 4; - const struct uwb_drp_alloc *alloc; - int cnt; - u16 zone_bm, mas_bm; - u8 zone; - u16 zone_mask; - - bitmap_zero(bm->bm, UWB_NUM_MAS); - - for (cnt = 0; cnt < numallocs; cnt++) { - alloc = &drp_ie->allocs[cnt]; - zone_bm = le16_to_cpu(alloc->zone_bm); - mas_bm = le16_to_cpu(alloc->mas_bm); - for (zone = 0; zone < UWB_NUM_ZONES; zone++) { - zone_mask = 1 << zone; - if (zone_bm & zone_mask) - uwb_drp_ie_single_zone_to_bm(bm, zone, mas_bm); - } - } -} - diff --git a/drivers/staging/uwb/drp.c b/drivers/staging/uwb/drp.c deleted file mode 100644 index 869987bede7b..000000000000 --- a/drivers/staging/uwb/drp.c +++ /dev/null @@ -1,842 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Dynamic Reservation Protocol handling - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include -#include "uwb-internal.h" - - -/* DRP Conflict Actions ([ECMA-368 2nd Edition] 17.4.6) */ -enum uwb_drp_conflict_action { - /* Reservation is maintained, no action needed */ - UWB_DRP_CONFLICT_MANTAIN = 0, - - /* the device shall not transmit frames in conflicting MASs in - * the following superframe. If the device is the reservation - * target, it shall also set the Reason Code in its DRP IE to - * Conflict in its beacon in the following superframe. - */ - UWB_DRP_CONFLICT_ACT1, - - /* the device shall not set the Reservation Status bit to ONE - * and shall not transmit frames in conflicting MASs. If the - * device is the reservation target, it shall also set the - * Reason Code in its DRP IE to Conflict. - */ - UWB_DRP_CONFLICT_ACT2, - - /* the device shall not transmit frames in conflicting MASs in - * the following superframe. It shall remove the conflicting - * MASs from the reservation or set the Reservation Status to - * ZERO in its beacon in the following superframe. If the - * device is the reservation target, it shall also set the - * Reason Code in its DRP IE to Conflict. - */ - UWB_DRP_CONFLICT_ACT3, -}; - - -static void uwb_rc_set_drp_cmd_done(struct uwb_rc *rc, void *arg, - struct uwb_rceb *reply, ssize_t reply_size) -{ - struct uwb_rc_evt_set_drp_ie *r = (struct uwb_rc_evt_set_drp_ie *)reply; - unsigned long flags; - - if (r != NULL) { - if (r->bResultCode != UWB_RC_RES_SUCCESS) - dev_err(&rc->uwb_dev.dev, "SET-DRP-IE failed: %s (%d)\n", - uwb_rc_strerror(r->bResultCode), r->bResultCode); - } else - dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: timeout\n"); - - spin_lock_irqsave(&rc->rsvs_lock, flags); - if (rc->set_drp_ie_pending > 1) { - rc->set_drp_ie_pending = 0; - uwb_rsv_queue_update(rc); - } else { - rc->set_drp_ie_pending = 0; - } - spin_unlock_irqrestore(&rc->rsvs_lock, flags); -} - -/** - * Construct and send the SET DRP IE - * - * @rc: UWB Host controller - * @returns: >= 0 number of bytes still available in the beacon - * < 0 errno code on error. - * - * See WUSB[8.6.2.7]: The host must set all the DRP IEs that it wants the - * device to include in its beacon at the same time. We thus have to - * traverse all reservations and include the DRP IEs of all PENDING - * and NEGOTIATED reservations in a SET DRP command for transmission. - * - * A DRP Availability IE is appended. - * - * rc->rsvs_mutex is held - * - * FIXME We currently ignore the returned value indicating the remaining space - * in beacon. This could be used to deny reservation requests earlier if - * determined that they would cause the beacon space to be exceeded. - */ -int uwb_rc_send_all_drp_ie(struct uwb_rc *rc) -{ - int result; - struct uwb_rc_cmd_set_drp_ie *cmd; - struct uwb_rsv *rsv; - struct uwb_rsv_move *mv; - int num_bytes = 0; - u8 *IEDataptr; - - result = -ENOMEM; - /* First traverse all reservations to determine memory needed. */ - list_for_each_entry(rsv, &rc->reservations, rc_node) { - if (rsv->drp_ie != NULL) { - num_bytes += rsv->drp_ie->hdr.length + 2; - if (uwb_rsv_has_two_drp_ies(rsv) && - (rsv->mv.companion_drp_ie != NULL)) { - mv = &rsv->mv; - num_bytes += - mv->companion_drp_ie->hdr.length + 2; - } - } - } - num_bytes += sizeof(rc->drp_avail.ie); - cmd = kzalloc(sizeof(*cmd) + num_bytes, GFP_KERNEL); - if (cmd == NULL) - goto error; - cmd->rccb.bCommandType = UWB_RC_CET_GENERAL; - cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_DRP_IE); - cmd->wIELength = num_bytes; - IEDataptr = (u8 *)&cmd->IEData[0]; - - /* FIXME: DRV avail IE is not always needed */ - /* put DRP avail IE first */ - memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie)); - IEDataptr += sizeof(struct uwb_ie_drp_avail); - - /* Next traverse all reservations to place IEs in allocated memory. */ - list_for_each_entry(rsv, &rc->reservations, rc_node) { - if (rsv->drp_ie != NULL) { - memcpy(IEDataptr, rsv->drp_ie, - rsv->drp_ie->hdr.length + 2); - IEDataptr += rsv->drp_ie->hdr.length + 2; - - if (uwb_rsv_has_two_drp_ies(rsv) && - (rsv->mv.companion_drp_ie != NULL)) { - mv = &rsv->mv; - memcpy(IEDataptr, mv->companion_drp_ie, - mv->companion_drp_ie->hdr.length + 2); - IEDataptr += - mv->companion_drp_ie->hdr.length + 2; - } - } - } - - result = uwb_rc_cmd_async(rc, "SET-DRP-IE", - &cmd->rccb, sizeof(*cmd) + num_bytes, - UWB_RC_CET_GENERAL, UWB_RC_CMD_SET_DRP_IE, - uwb_rc_set_drp_cmd_done, NULL); - - rc->set_drp_ie_pending = 1; - - kfree(cmd); -error: - return result; -} - -/* - * Evaluate the action to perform using conflict resolution rules - * - * Return a uwb_drp_conflict_action. - */ -static int evaluate_conflict_action(struct uwb_ie_drp *ext_drp_ie, int ext_beacon_slot, - struct uwb_rsv *rsv, int our_status) -{ - int our_tie_breaker = rsv->tiebreaker; - int our_type = rsv->type; - int our_beacon_slot = rsv->rc->uwb_dev.beacon_slot; - - int ext_tie_breaker = uwb_ie_drp_tiebreaker(ext_drp_ie); - int ext_status = uwb_ie_drp_status(ext_drp_ie); - int ext_type = uwb_ie_drp_type(ext_drp_ie); - - - /* [ECMA-368 2nd Edition] 17.4.6 */ - if (ext_type == UWB_DRP_TYPE_PCA && our_type == UWB_DRP_TYPE_PCA) { - return UWB_DRP_CONFLICT_MANTAIN; - } - - /* [ECMA-368 2nd Edition] 17.4.6-1 */ - if (our_type == UWB_DRP_TYPE_ALIEN_BP) { - return UWB_DRP_CONFLICT_MANTAIN; - } - - /* [ECMA-368 2nd Edition] 17.4.6-2 */ - if (ext_type == UWB_DRP_TYPE_ALIEN_BP) { - /* here we know our_type != UWB_DRP_TYPE_ALIEN_BP */ - return UWB_DRP_CONFLICT_ACT1; - } - - /* [ECMA-368 2nd Edition] 17.4.6-3 */ - if (our_status == 0 && ext_status == 1) { - return UWB_DRP_CONFLICT_ACT2; - } - - /* [ECMA-368 2nd Edition] 17.4.6-4 */ - if (our_status == 1 && ext_status == 0) { - return UWB_DRP_CONFLICT_MANTAIN; - } - - /* [ECMA-368 2nd Edition] 17.4.6-5a */ - if (our_tie_breaker == ext_tie_breaker && - our_beacon_slot < ext_beacon_slot) { - return UWB_DRP_CONFLICT_MANTAIN; - } - - /* [ECMA-368 2nd Edition] 17.4.6-5b */ - if (our_tie_breaker != ext_tie_breaker && - our_beacon_slot > ext_beacon_slot) { - return UWB_DRP_CONFLICT_MANTAIN; - } - - if (our_status == 0) { - if (our_tie_breaker == ext_tie_breaker) { - /* [ECMA-368 2nd Edition] 17.4.6-6a */ - if (our_beacon_slot > ext_beacon_slot) { - return UWB_DRP_CONFLICT_ACT2; - } - } else { - /* [ECMA-368 2nd Edition] 17.4.6-6b */ - if (our_beacon_slot < ext_beacon_slot) { - return UWB_DRP_CONFLICT_ACT2; - } - } - } else { - if (our_tie_breaker == ext_tie_breaker) { - /* [ECMA-368 2nd Edition] 17.4.6-7a */ - if (our_beacon_slot > ext_beacon_slot) { - return UWB_DRP_CONFLICT_ACT3; - } - } else { - /* [ECMA-368 2nd Edition] 17.4.6-7b */ - if (our_beacon_slot < ext_beacon_slot) { - return UWB_DRP_CONFLICT_ACT3; - } - } - } - return UWB_DRP_CONFLICT_MANTAIN; -} - -static void handle_conflict_normal(struct uwb_ie_drp *drp_ie, - int ext_beacon_slot, - struct uwb_rsv *rsv, - struct uwb_mas_bm *conflicting_mas) -{ - struct uwb_rc *rc = rsv->rc; - struct uwb_rsv_move *mv = &rsv->mv; - struct uwb_drp_backoff_win *bow = &rc->bow; - int action; - - action = evaluate_conflict_action(drp_ie, ext_beacon_slot, rsv, uwb_rsv_status(rsv)); - - if (uwb_rsv_is_owner(rsv)) { - switch(action) { - case UWB_DRP_CONFLICT_ACT2: - /* try move */ - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_TO_BE_MOVED); - if (bow->can_reserve_extra_mases == false) - uwb_rsv_backoff_win_increment(rc); - - break; - case UWB_DRP_CONFLICT_ACT3: - uwb_rsv_backoff_win_increment(rc); - /* drop some mases with reason modified */ - /* put in the companion the mases to be dropped */ - bitmap_and(mv->companion_mas.bm, rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS); - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED); - default: - break; - } - } else { - switch(action) { - case UWB_DRP_CONFLICT_ACT2: - case UWB_DRP_CONFLICT_ACT3: - uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT); - default: - break; - } - - } - -} - -static void handle_conflict_expanding(struct uwb_ie_drp *drp_ie, int ext_beacon_slot, - struct uwb_rsv *rsv, bool companion_only, - struct uwb_mas_bm *conflicting_mas) -{ - struct uwb_rc *rc = rsv->rc; - struct uwb_drp_backoff_win *bow = &rc->bow; - struct uwb_rsv_move *mv = &rsv->mv; - int action; - - if (companion_only) { - /* status of companion is 0 at this point */ - action = evaluate_conflict_action(drp_ie, ext_beacon_slot, rsv, 0); - if (uwb_rsv_is_owner(rsv)) { - switch(action) { - case UWB_DRP_CONFLICT_ACT2: - case UWB_DRP_CONFLICT_ACT3: - uwb_rsv_set_state(rsv, - UWB_RSV_STATE_O_ESTABLISHED); - rsv->needs_release_companion_mas = false; - if (bow->can_reserve_extra_mases == false) - uwb_rsv_backoff_win_increment(rc); - uwb_drp_avail_release(rsv->rc, - &rsv->mv.companion_mas); - } - } else { /* rsv is target */ - switch(action) { - case UWB_DRP_CONFLICT_ACT2: - case UWB_DRP_CONFLICT_ACT3: - uwb_rsv_set_state(rsv, - UWB_RSV_STATE_T_EXPANDING_CONFLICT); - /* send_drp_avail_ie = true; */ - } - } - } else { /* also base part of the reservation is conflicting */ - if (uwb_rsv_is_owner(rsv)) { - uwb_rsv_backoff_win_increment(rc); - /* remove companion part */ - uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); - - /* drop some mases with reason modified */ - - /* put in the companion the mases to be dropped */ - bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, - conflicting_mas->bm, UWB_NUM_MAS); - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED); - } else { /* it is a target rsv */ - uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT); - /* send_drp_avail_ie = true; */ - } - } -} - -static void uwb_drp_handle_conflict_rsv(struct uwb_rc *rc, struct uwb_rsv *rsv, - struct uwb_rc_evt_drp *drp_evt, - struct uwb_ie_drp *drp_ie, - struct uwb_mas_bm *conflicting_mas) -{ - struct uwb_rsv_move *mv; - - /* check if the conflicting reservation has two drp_ies */ - if (uwb_rsv_has_two_drp_ies(rsv)) { - mv = &rsv->mv; - if (bitmap_intersects(rsv->mas.bm, conflicting_mas->bm, - UWB_NUM_MAS)) { - handle_conflict_expanding(drp_ie, - drp_evt->beacon_slot_number, - rsv, false, conflicting_mas); - } else { - if (bitmap_intersects(mv->companion_mas.bm, - conflicting_mas->bm, UWB_NUM_MAS)) { - handle_conflict_expanding( - drp_ie, drp_evt->beacon_slot_number, - rsv, true, conflicting_mas); - } - } - } else if (bitmap_intersects(rsv->mas.bm, conflicting_mas->bm, - UWB_NUM_MAS)) { - handle_conflict_normal(drp_ie, drp_evt->beacon_slot_number, - rsv, conflicting_mas); - } -} - -static void uwb_drp_handle_all_conflict_rsv(struct uwb_rc *rc, - struct uwb_rc_evt_drp *drp_evt, - struct uwb_ie_drp *drp_ie, - struct uwb_mas_bm *conflicting_mas) -{ - struct uwb_rsv *rsv; - - list_for_each_entry(rsv, &rc->reservations, rc_node) { - uwb_drp_handle_conflict_rsv(rc, rsv, drp_evt, drp_ie, - conflicting_mas); - } -} - -static void uwb_drp_process_target_accepted(struct uwb_rc *rc, - struct uwb_rsv *rsv, struct uwb_rc_evt_drp *drp_evt, - struct uwb_ie_drp *drp_ie, struct uwb_mas_bm *mas) -{ - struct uwb_rsv_move *mv = &rsv->mv; - int status; - - status = uwb_ie_drp_status(drp_ie); - - if (rsv->state == UWB_RSV_STATE_T_CONFLICT) { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT); - return; - } - - if (rsv->state == UWB_RSV_STATE_T_EXPANDING_ACCEPTED) { - /* drp_ie is companion */ - if (!bitmap_equal(rsv->mas.bm, mas->bm, UWB_NUM_MAS)) { - /* stroke companion */ - uwb_rsv_set_state(rsv, - UWB_RSV_STATE_T_EXPANDING_ACCEPTED); - } - } else { - if (!bitmap_equal(rsv->mas.bm, mas->bm, UWB_NUM_MAS)) { - if (uwb_drp_avail_reserve_pending(rc, mas) == -EBUSY) { - /* FIXME: there is a conflict, find - * the conflicting reservations and - * take a sensible action. Consider - * that in drp_ie there is the - * "neighbour" */ - uwb_drp_handle_all_conflict_rsv(rc, drp_evt, - drp_ie, mas); - } else { - /* accept the extra reservation */ - bitmap_copy(mv->companion_mas.bm, mas->bm, - UWB_NUM_MAS); - uwb_rsv_set_state(rsv, - UWB_RSV_STATE_T_EXPANDING_ACCEPTED); - } - } else { - if (status) { - uwb_rsv_set_state(rsv, - UWB_RSV_STATE_T_ACCEPTED); - } - } - - } -} - -/* - * Based on the DRP IE, transition a target reservation to a new - * state. - */ -static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv, - struct uwb_ie_drp *drp_ie, struct uwb_rc_evt_drp *drp_evt) -{ - struct device *dev = &rc->uwb_dev.dev; - struct uwb_rsv_move *mv = &rsv->mv; - int status; - enum uwb_drp_reason reason_code; - struct uwb_mas_bm mas; - - status = uwb_ie_drp_status(drp_ie); - reason_code = uwb_ie_drp_reason_code(drp_ie); - uwb_drp_ie_to_bm(&mas, drp_ie); - - switch (reason_code) { - case UWB_DRP_REASON_ACCEPTED: - uwb_drp_process_target_accepted(rc, rsv, drp_evt, drp_ie, &mas); - break; - - case UWB_DRP_REASON_MODIFIED: - /* check to see if we have already modified the reservation */ - if (bitmap_equal(rsv->mas.bm, mas.bm, UWB_NUM_MAS)) { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); - break; - } - - /* find if the owner wants to expand or reduce */ - if (bitmap_subset(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) { - /* owner is reducing */ - bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, mas.bm, - UWB_NUM_MAS); - uwb_drp_avail_release(rsv->rc, &mv->companion_mas); - } - - bitmap_copy(rsv->mas.bm, mas.bm, UWB_NUM_MAS); - uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_RESIZED); - break; - default: - dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", - reason_code, status); - } -} - -static void uwb_drp_process_owner_accepted(struct uwb_rsv *rsv, - struct uwb_mas_bm *mas) -{ - struct uwb_rsv_move *mv = &rsv->mv; - - switch (rsv->state) { - case UWB_RSV_STATE_O_PENDING: - case UWB_RSV_STATE_O_INITIATED: - case UWB_RSV_STATE_O_ESTABLISHED: - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); - break; - case UWB_RSV_STATE_O_MODIFIED: - if (bitmap_equal(mas->bm, rsv->mas.bm, UWB_NUM_MAS)) - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); - else - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED); - break; - - case UWB_RSV_STATE_O_MOVE_REDUCING: /* shouldn' t be a problem */ - if (bitmap_equal(mas->bm, rsv->mas.bm, UWB_NUM_MAS)) - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); - else - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); - break; - case UWB_RSV_STATE_O_MOVE_EXPANDING: - if (bitmap_equal(mas->bm, mv->companion_mas.bm, UWB_NUM_MAS)) { - /* Companion reservation accepted */ - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); - } else { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); - } - break; - case UWB_RSV_STATE_O_MOVE_COMBINING: - if (bitmap_equal(mas->bm, rsv->mas.bm, UWB_NUM_MAS)) - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); - else - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); - break; - default: - break; - } -} -/* - * Based on the DRP IE, transition an owner reservation to a new - * state. - */ -static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv, - struct uwb_dev *src, struct uwb_ie_drp *drp_ie, - struct uwb_rc_evt_drp *drp_evt) -{ - struct device *dev = &rc->uwb_dev.dev; - int status; - enum uwb_drp_reason reason_code; - struct uwb_mas_bm mas; - - status = uwb_ie_drp_status(drp_ie); - reason_code = uwb_ie_drp_reason_code(drp_ie); - uwb_drp_ie_to_bm(&mas, drp_ie); - - if (status) { - switch (reason_code) { - case UWB_DRP_REASON_ACCEPTED: - uwb_drp_process_owner_accepted(rsv, &mas); - break; - default: - dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", - reason_code, status); - } - } else { - switch (reason_code) { - case UWB_DRP_REASON_PENDING: - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_PENDING); - break; - case UWB_DRP_REASON_DENIED: - uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); - break; - case UWB_DRP_REASON_CONFLICT: - /* resolve the conflict */ - bitmap_complement(mas.bm, src->last_availability_bm, - UWB_NUM_MAS); - uwb_drp_handle_conflict_rsv(rc, rsv, drp_evt, drp_ie, &mas); - break; - default: - dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", - reason_code, status); - } - } -} - -static void uwb_cnflt_alien_stroke_timer(struct uwb_cnflt_alien *cnflt) -{ - unsigned timeout_us = UWB_MAX_LOST_BEACONS * UWB_SUPERFRAME_LENGTH_US; - mod_timer(&cnflt->timer, jiffies + usecs_to_jiffies(timeout_us)); -} - -static void uwb_cnflt_update_work(struct work_struct *work) -{ - struct uwb_cnflt_alien *cnflt = container_of(work, - struct uwb_cnflt_alien, - cnflt_update_work); - struct uwb_cnflt_alien *c; - struct uwb_rc *rc = cnflt->rc; - - unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; - - mutex_lock(&rc->rsvs_mutex); - - list_del(&cnflt->rc_node); - - /* update rc global conflicting alien bitmap */ - bitmap_zero(rc->cnflt_alien_bitmap.bm, UWB_NUM_MAS); - - list_for_each_entry(c, &rc->cnflt_alien_list, rc_node) { - bitmap_or(rc->cnflt_alien_bitmap.bm, rc->cnflt_alien_bitmap.bm, - c->mas.bm, UWB_NUM_MAS); - } - - queue_delayed_work(rc->rsv_workq, &rc->rsv_alien_bp_work, - usecs_to_jiffies(delay_us)); - - kfree(cnflt); - mutex_unlock(&rc->rsvs_mutex); -} - -static void uwb_cnflt_timer(struct timer_list *t) -{ - struct uwb_cnflt_alien *cnflt = from_timer(cnflt, t, timer); - - queue_work(cnflt->rc->rsv_workq, &cnflt->cnflt_update_work); -} - -/* - * We have received an DRP_IE of type Alien BP and we need to make - * sure we do not transmit in conflicting MASs. - */ -static void uwb_drp_handle_alien_drp(struct uwb_rc *rc, struct uwb_ie_drp *drp_ie) -{ - struct device *dev = &rc->uwb_dev.dev; - struct uwb_mas_bm mas; - struct uwb_cnflt_alien *cnflt; - unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; - - uwb_drp_ie_to_bm(&mas, drp_ie); - - list_for_each_entry(cnflt, &rc->cnflt_alien_list, rc_node) { - if (bitmap_equal(cnflt->mas.bm, mas.bm, UWB_NUM_MAS)) { - /* Existing alien BP reservation conflicting - * bitmap, just reset the timer */ - uwb_cnflt_alien_stroke_timer(cnflt); - return; - } - } - - /* New alien BP reservation conflicting bitmap */ - - /* alloc and initialize new uwb_cnflt_alien */ - cnflt = kzalloc(sizeof(struct uwb_cnflt_alien), GFP_KERNEL); - if (!cnflt) { - dev_err(dev, "failed to alloc uwb_cnflt_alien struct\n"); - return; - } - - INIT_LIST_HEAD(&cnflt->rc_node); - timer_setup(&cnflt->timer, uwb_cnflt_timer, 0); - - cnflt->rc = rc; - INIT_WORK(&cnflt->cnflt_update_work, uwb_cnflt_update_work); - - bitmap_copy(cnflt->mas.bm, mas.bm, UWB_NUM_MAS); - - list_add_tail(&cnflt->rc_node, &rc->cnflt_alien_list); - - /* update rc global conflicting alien bitmap */ - bitmap_or(rc->cnflt_alien_bitmap.bm, rc->cnflt_alien_bitmap.bm, mas.bm, UWB_NUM_MAS); - - queue_delayed_work(rc->rsv_workq, &rc->rsv_alien_bp_work, usecs_to_jiffies(delay_us)); - - /* start the timer */ - uwb_cnflt_alien_stroke_timer(cnflt); -} - -static void uwb_drp_process_not_involved(struct uwb_rc *rc, - struct uwb_rc_evt_drp *drp_evt, - struct uwb_ie_drp *drp_ie) -{ - struct uwb_mas_bm mas; - - uwb_drp_ie_to_bm(&mas, drp_ie); - uwb_drp_handle_all_conflict_rsv(rc, drp_evt, drp_ie, &mas); -} - -static void uwb_drp_process_involved(struct uwb_rc *rc, struct uwb_dev *src, - struct uwb_rc_evt_drp *drp_evt, - struct uwb_ie_drp *drp_ie) -{ - struct uwb_rsv *rsv; - - rsv = uwb_rsv_find(rc, src, drp_ie); - if (!rsv) { - /* - * No reservation? It's either for a recently - * terminated reservation; or the DRP IE couldn't be - * processed (e.g., an invalid IE or out of memory). - */ - return; - } - - /* - * Do nothing with DRP IEs for reservations that have been - * terminated. - */ - if (rsv->state == UWB_RSV_STATE_NONE) { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); - return; - } - - if (uwb_ie_drp_owner(drp_ie)) - uwb_drp_process_target(rc, rsv, drp_ie, drp_evt); - else - uwb_drp_process_owner(rc, rsv, src, drp_ie, drp_evt); - -} - - -static bool uwb_drp_involves_us(struct uwb_rc *rc, struct uwb_ie_drp *drp_ie) -{ - return uwb_dev_addr_cmp(&rc->uwb_dev.dev_addr, &drp_ie->dev_addr) == 0; -} - -/* - * Process a received DRP IE. - */ -static void uwb_drp_process(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, - struct uwb_dev *src, struct uwb_ie_drp *drp_ie) -{ - if (uwb_ie_drp_type(drp_ie) == UWB_DRP_TYPE_ALIEN_BP) - uwb_drp_handle_alien_drp(rc, drp_ie); - else if (uwb_drp_involves_us(rc, drp_ie)) - uwb_drp_process_involved(rc, src, drp_evt, drp_ie); - else - uwb_drp_process_not_involved(rc, drp_evt, drp_ie); -} - -/* - * Process a received DRP Availability IE - */ -static void uwb_drp_availability_process(struct uwb_rc *rc, struct uwb_dev *src, - struct uwb_ie_drp_avail *drp_availability_ie) -{ - bitmap_copy(src->last_availability_bm, - drp_availability_ie->bmp, UWB_NUM_MAS); -} - -/* - * Process all the DRP IEs (both DRP IEs and the DRP Availability IE) - * from a device. - */ -static -void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, - size_t ielen, struct uwb_dev *src_dev) -{ - struct device *dev = &rc->uwb_dev.dev; - struct uwb_ie_hdr *ie_hdr; - void *ptr; - - ptr = drp_evt->ie_data; - for (;;) { - ie_hdr = uwb_ie_next(&ptr, &ielen); - if (!ie_hdr) - break; - - switch (ie_hdr->element_id) { - case UWB_IE_DRP_AVAILABILITY: - uwb_drp_availability_process(rc, src_dev, (struct uwb_ie_drp_avail *)ie_hdr); - break; - case UWB_IE_DRP: - uwb_drp_process(rc, drp_evt, src_dev, (struct uwb_ie_drp *)ie_hdr); - break; - default: - dev_warn(dev, "unexpected IE in DRP notification\n"); - break; - } - } - - if (ielen > 0) - dev_warn(dev, "%d octets remaining in DRP notification\n", - (int)ielen); -} - -/** - * uwbd_evt_handle_rc_drp - handle a DRP_IE event - * @evt: the DRP_IE event from the radio controller - * - * This processes DRP notifications from the radio controller, either - * initiating a new reservation or transitioning an existing - * reservation into a different state. - * - * DRP notifications can occur for three different reasons: - * - * - UWB_DRP_NOTIF_DRP_IE_RECVD: one or more DRP IEs with the RC as - * the target or source have been received. - * - * These DRP IEs could be new or for an existing reservation. - * - * If the DRP IE for an existing reservation ceases to be to - * received for at least mMaxLostBeacons, the reservation should be - * considered to be terminated. Note that the TERMINATE reason (see - * below) may not always be signalled (e.g., the remote device has - * two or more reservations established with the RC). - * - * - UWB_DRP_NOTIF_CONFLICT: DRP IEs from any device in the beacon - * group conflict with the RC's reservations. - * - * - UWB_DRP_NOTIF_TERMINATE: DRP IEs are no longer being received - * from a device (i.e., it's terminated all reservations). - * - * Only the software state of the reservations is changed; the setting - * of the radio controller's DRP IEs is done after all the events in - * an event buffer are processed. This saves waiting multiple times - * for the SET_DRP_IE command to complete. - */ -int uwbd_evt_handle_rc_drp(struct uwb_event *evt) -{ - struct device *dev = &evt->rc->uwb_dev.dev; - struct uwb_rc *rc = evt->rc; - struct uwb_rc_evt_drp *drp_evt; - size_t ielength, bytes_left; - struct uwb_dev_addr src_addr; - struct uwb_dev *src_dev; - - /* Is there enough data to decode the event (and any IEs in - its payload)? */ - if (evt->notif.size < sizeof(*drp_evt)) { - dev_err(dev, "DRP event: Not enough data to decode event " - "[%zu bytes left, %zu needed]\n", - evt->notif.size, sizeof(*drp_evt)); - return 0; - } - bytes_left = evt->notif.size - sizeof(*drp_evt); - drp_evt = container_of(evt->notif.rceb, struct uwb_rc_evt_drp, rceb); - ielength = le16_to_cpu(drp_evt->ie_length); - if (bytes_left != ielength) { - dev_err(dev, "DRP event: Not enough data in payload [%zu" - "bytes left, %zu declared in the event]\n", - bytes_left, ielength); - return 0; - } - - memcpy(src_addr.data, &drp_evt->src_addr, sizeof(src_addr)); - src_dev = uwb_dev_get_by_devaddr(rc, &src_addr); - if (!src_dev) { - /* - * A DRP notification from an unrecognized device. - * - * This is probably from a WUSB device that doesn't - * have an EUI-48 and therefore doesn't show up in the - * UWB device database. It's safe to simply ignore - * these. - */ - return 0; - } - - mutex_lock(&rc->rsvs_mutex); - - /* We do not distinguish from the reason */ - uwb_drp_process_all(rc, drp_evt, ielength, src_dev); - - mutex_unlock(&rc->rsvs_mutex); - - uwb_dev_put(src_dev); - return 0; -} diff --git a/drivers/staging/uwb/est.c b/drivers/staging/uwb/est.c deleted file mode 100644 index d4141ffdd775..000000000000 --- a/drivers/staging/uwb/est.c +++ /dev/null @@ -1,450 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band Radio Control - * Event Size Tables management - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - * - * Infrastructure, code and data tables for guessing the size of - * events received on the notification endpoints of UWB radio - * controllers. - * - * You define a table of events and for each, its size and how to get - * the extra size. - * - * ENTRY POINTS: - * - * uwb_est_{init/destroy}(): To initialize/release the EST subsystem. - * - * uwb_est_[u]register(): To un/register event size tables - * uwb_est_grow() - * - * uwb_est_find_size(): Get the size of an event - * uwb_est_get_size() - */ -#include -#include -#include - -#include "uwb-internal.h" - -struct uwb_est { - u16 type_event_high; - u16 vendor, product; - u8 entries; - const struct uwb_est_entry *entry; -}; - -static struct uwb_est *uwb_est; -static u8 uwb_est_size; -static u8 uwb_est_used; -static DEFINE_RWLOCK(uwb_est_lock); - -/** - * WUSB Standard Event Size Table, HWA-RC interface - * - * Sizes for events and notifications type 0 (general), high nibble 0. - */ -static -struct uwb_est_entry uwb_est_00_00xx[] = { - [UWB_RC_EVT_IE_RCV] = { - .size = sizeof(struct uwb_rc_evt_ie_rcv), - .offset = 1 + offsetof(struct uwb_rc_evt_ie_rcv, wIELength), - }, - [UWB_RC_EVT_BEACON] = { - .size = sizeof(struct uwb_rc_evt_beacon), - .offset = 1 + offsetof(struct uwb_rc_evt_beacon, wBeaconInfoLength), - }, - [UWB_RC_EVT_BEACON_SIZE] = { - .size = sizeof(struct uwb_rc_evt_beacon_size), - }, - [UWB_RC_EVT_BPOIE_CHANGE] = { - .size = sizeof(struct uwb_rc_evt_bpoie_change), - .offset = 1 + offsetof(struct uwb_rc_evt_bpoie_change, - wBPOIELength), - }, - [UWB_RC_EVT_BP_SLOT_CHANGE] = { - .size = sizeof(struct uwb_rc_evt_bp_slot_change), - }, - [UWB_RC_EVT_BP_SWITCH_IE_RCV] = { - .size = sizeof(struct uwb_rc_evt_bp_switch_ie_rcv), - .offset = 1 + offsetof(struct uwb_rc_evt_bp_switch_ie_rcv, wIELength), - }, - [UWB_RC_EVT_DEV_ADDR_CONFLICT] = { - .size = sizeof(struct uwb_rc_evt_dev_addr_conflict), - }, - [UWB_RC_EVT_DRP_AVAIL] = { - .size = sizeof(struct uwb_rc_evt_drp_avail) - }, - [UWB_RC_EVT_DRP] = { - .size = sizeof(struct uwb_rc_evt_drp), - .offset = 1 + offsetof(struct uwb_rc_evt_drp, ie_length), - }, - [UWB_RC_EVT_BP_SWITCH_STATUS] = { - .size = sizeof(struct uwb_rc_evt_bp_switch_status), - }, - [UWB_RC_EVT_CMD_FRAME_RCV] = { - .size = sizeof(struct uwb_rc_evt_cmd_frame_rcv), - .offset = 1 + offsetof(struct uwb_rc_evt_cmd_frame_rcv, dataLength), - }, - [UWB_RC_EVT_CHANNEL_CHANGE_IE_RCV] = { - .size = sizeof(struct uwb_rc_evt_channel_change_ie_rcv), - .offset = 1 + offsetof(struct uwb_rc_evt_channel_change_ie_rcv, wIELength), - }, - [UWB_RC_CMD_CHANNEL_CHANGE] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_DEV_ADDR_MGMT] = { - .size = sizeof(struct uwb_rc_evt_dev_addr_mgmt) }, - [UWB_RC_CMD_GET_IE] = { - .size = sizeof(struct uwb_rc_evt_get_ie), - .offset = 1 + offsetof(struct uwb_rc_evt_get_ie, wIELength), - }, - [UWB_RC_CMD_RESET] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_SCAN] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_SET_BEACON_FILTER] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_SET_DRP_IE] = { - .size = sizeof(struct uwb_rc_evt_set_drp_ie), - }, - [UWB_RC_CMD_SET_IE] = { - .size = sizeof(struct uwb_rc_evt_set_ie), - }, - [UWB_RC_CMD_SET_NOTIFICATION_FILTER] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_SET_TX_POWER] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_SLEEP] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_START_BEACON] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_STOP_BEACON] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_BP_MERGE] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_SEND_COMMAND_FRAME] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, - [UWB_RC_CMD_SET_ASIE_NOTIF] = { - .size = sizeof(struct uwb_rc_evt_confirm), - }, -}; - -static -struct uwb_est_entry uwb_est_01_00xx[] = { - [UWB_RC_DAA_ENERGY_DETECTED] = { - .size = sizeof(struct uwb_rc_evt_daa_energy_detected), - }, - [UWB_RC_SET_DAA_ENERGY_MASK] = { - .size = sizeof(struct uwb_rc_evt_set_daa_energy_mask), - }, - [UWB_RC_SET_NOTIFICATION_FILTER_EX] = { - .size = sizeof(struct uwb_rc_evt_set_notification_filter_ex), - }, -}; - -/** - * Initialize the EST subsystem - * - * Register the standard tables also. - * - * FIXME: tag init - */ -int uwb_est_create(void) -{ - int result; - - uwb_est_size = 2; - uwb_est_used = 0; - uwb_est = kcalloc(uwb_est_size, sizeof(uwb_est[0]), GFP_KERNEL); - if (uwb_est == NULL) - return -ENOMEM; - - result = uwb_est_register(UWB_RC_CET_GENERAL, 0, 0xffff, 0xffff, - uwb_est_00_00xx, ARRAY_SIZE(uwb_est_00_00xx)); - if (result < 0) - goto out; - result = uwb_est_register(UWB_RC_CET_EX_TYPE_1, 0, 0xffff, 0xffff, - uwb_est_01_00xx, ARRAY_SIZE(uwb_est_01_00xx)); -out: - return result; -} - - -/** Clean it up */ -void uwb_est_destroy(void) -{ - kfree(uwb_est); - uwb_est = NULL; - uwb_est_size = uwb_est_used = 0; -} - - -/** - * Double the capacity of the EST table - * - * @returns 0 if ok, < 0 errno no error. - */ -static -int uwb_est_grow(void) -{ - size_t actual_size = uwb_est_size * sizeof(uwb_est[0]); - void *new = kmalloc_array(2, actual_size, GFP_ATOMIC); - if (new == NULL) - return -ENOMEM; - memcpy(new, uwb_est, actual_size); - memset(new + actual_size, 0, actual_size); - kfree(uwb_est); - uwb_est = new; - uwb_est_size *= 2; - return 0; -} - - -/** - * Register an event size table - * - * Makes room for it if the table is full, and then inserts it in the - * right position (entries are sorted by type, event_high, vendor and - * then product). - * - * @vendor: vendor code for matching against the device (0x0000 and - * 0xffff mean any); use 0x0000 to force all to match without - * checking possible vendor specific ones, 0xfffff to match - * after checking vendor specific ones. - * - * @product: product code from that vendor; same matching rules, use - * 0x0000 for not allowing vendor specific matches, 0xffff - * for allowing. - * - * This arragement just makes the tables sort differenty. Because the - * table is sorted by growing type-event_high-vendor-product, a zero - * vendor will match before than a 0x456a vendor, that will match - * before a 0xfffff vendor. - * - * @returns 0 if ok, < 0 errno on error (-ENOENT if not found). - */ -/* FIXME: add bus type to vendor/product code */ -int uwb_est_register(u8 type, u8 event_high, u16 vendor, u16 product, - const struct uwb_est_entry *entry, size_t entries) -{ - unsigned long flags; - unsigned itr; - int result = 0; - - write_lock_irqsave(&uwb_est_lock, flags); - if (uwb_est_used == uwb_est_size) { - result = uwb_est_grow(); - if (result < 0) - goto out; - } - /* Find the right spot to insert it in */ - for (itr = 0; itr < uwb_est_used; itr++) - if (uwb_est[itr].type_event_high < type - && uwb_est[itr].vendor < vendor - && uwb_est[itr].product < product) - break; - - /* Shift others to make room for the new one? */ - if (itr < uwb_est_used) - memmove(&uwb_est[itr+1], &uwb_est[itr], uwb_est_used - itr); - uwb_est[itr].type_event_high = type << 8 | event_high; - uwb_est[itr].vendor = vendor; - uwb_est[itr].product = product; - uwb_est[itr].entry = entry; - uwb_est[itr].entries = entries; - uwb_est_used++; -out: - write_unlock_irqrestore(&uwb_est_lock, flags); - return result; -} -EXPORT_SYMBOL_GPL(uwb_est_register); - - -/** - * Unregister an event size table - * - * This just removes the specified entry and moves the ones after it - * to fill in the gap. This is needed to keep the list sorted; no - * reallocation is done to reduce the size of the table. - * - * We unregister by all the data we used to register instead of by - * pointer to the @entry array because we might have used the same - * table for a bunch of IDs (for example). - * - * @returns 0 if ok, < 0 errno on error (-ENOENT if not found). - */ -int uwb_est_unregister(u8 type, u8 event_high, u16 vendor, u16 product, - const struct uwb_est_entry *entry, size_t entries) -{ - unsigned long flags; - unsigned itr; - struct uwb_est est_cmp = { - .type_event_high = type << 8 | event_high, - .vendor = vendor, - .product = product, - .entry = entry, - .entries = entries - }; - write_lock_irqsave(&uwb_est_lock, flags); - for (itr = 0; itr < uwb_est_used; itr++) - if (!memcmp(&uwb_est[itr], &est_cmp, sizeof(est_cmp))) - goto found; - write_unlock_irqrestore(&uwb_est_lock, flags); - return -ENOENT; - -found: - if (itr < uwb_est_used - 1) /* Not last one? move ones above */ - memmove(&uwb_est[itr], &uwb_est[itr+1], uwb_est_used - itr - 1); - uwb_est_used--; - write_unlock_irqrestore(&uwb_est_lock, flags); - return 0; -} -EXPORT_SYMBOL_GPL(uwb_est_unregister); - - -/** - * Get the size of an event from a table - * - * @rceb: pointer to the buffer with the event - * @rceb_size: size of the area pointed to by @rceb in bytes. - * @returns: > 0 Size of the event - * -ENOSPC An area big enough was not provided to look - * ahead into the event's guts and guess the size. - * -EINVAL Unknown event code (wEvent). - * - * This will look at the received RCEB and guess what is the total - * size. For variable sized events, it will look further ahead into - * their length field to see how much data should be read. - * - * Note this size is *not* final--the neh (Notification/Event Handle) - * might specificy an extra size to add. - */ -static -ssize_t uwb_est_get_size(struct uwb_rc *uwb_rc, struct uwb_est *est, - u8 event_low, const struct uwb_rceb *rceb, - size_t rceb_size) -{ - unsigned offset; - ssize_t size; - struct device *dev = &uwb_rc->uwb_dev.dev; - const struct uwb_est_entry *entry; - - size = -ENOENT; - if (event_low >= est->entries) { /* in range? */ - dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: event %u out of range\n", - est, est->type_event_high, est->vendor, est->product, - est->entries, event_low); - goto out; - } - size = -ENOENT; - entry = &est->entry[event_low]; - if (entry->size == 0 && entry->offset == 0) { /* unknown? */ - dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: event %u unknown\n", - est, est->type_event_high, est->vendor, est->product, - est->entries, event_low); - goto out; - } - offset = entry->offset; /* extra fries with that? */ - if (offset == 0) - size = entry->size; - else { - /* Ops, got an extra size field at 'offset'--read it */ - const void *ptr = rceb; - size_t type_size = 0; - offset--; - size = -ENOSPC; /* enough data for more? */ - switch (entry->type) { - case UWB_EST_16: type_size = sizeof(__le16); break; - case UWB_EST_8: type_size = sizeof(u8); break; - default: BUG(); - } - if (offset + type_size > rceb_size) { - dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: " - "not enough data to read extra size\n", - est, est->type_event_high, est->vendor, - est->product, est->entries); - goto out; - } - size = entry->size; - ptr += offset; - switch (entry->type) { - case UWB_EST_16: size += le16_to_cpu(*(__le16 *)ptr); break; - case UWB_EST_8: size += *(u8 *)ptr; break; - default: BUG(); - } - } -out: - return size; -} - - -/** - * Guesses the size of a WA event - * - * @rceb: pointer to the buffer with the event - * @rceb_size: size of the area pointed to by @rceb in bytes. - * @returns: > 0 Size of the event - * -ENOSPC An area big enough was not provided to look - * ahead into the event's guts and guess the size. - * -EINVAL Unknown event code (wEvent). - * - * This will look at the received RCEB and guess what is the total - * size by checking all the tables registered with - * uwb_est_register(). For variable sized events, it will look further - * ahead into their length field to see how much data should be read. - * - * Note this size is *not* final--the neh (Notification/Event Handle) - * might specificy an extra size to add or replace. - */ -ssize_t uwb_est_find_size(struct uwb_rc *rc, const struct uwb_rceb *rceb, - size_t rceb_size) -{ - /* FIXME: add vendor/product data */ - ssize_t size; - struct device *dev = &rc->uwb_dev.dev; - unsigned long flags; - unsigned itr; - u16 type_event_high, event; - - read_lock_irqsave(&uwb_est_lock, flags); - size = -ENOSPC; - if (rceb_size < sizeof(*rceb)) - goto out; - event = le16_to_cpu(rceb->wEvent); - type_event_high = rceb->bEventType << 8 | (event & 0xff00) >> 8; - for (itr = 0; itr < uwb_est_used; itr++) { - if (uwb_est[itr].type_event_high != type_event_high) - continue; - size = uwb_est_get_size(rc, &uwb_est[itr], - event & 0x00ff, rceb, rceb_size); - /* try more tables that might handle the same type */ - if (size != -ENOENT) - goto out; - } - dev_dbg(dev, - "event 0x%02x/%04x/%02x: no handlers available; RCEB %4ph\n", - (unsigned) rceb->bEventType, - (unsigned) le16_to_cpu(rceb->wEvent), - (unsigned) rceb->bEventContext, - rceb); - size = -ENOENT; -out: - read_unlock_irqrestore(&uwb_est_lock, flags); - return size; -} -EXPORT_SYMBOL_GPL(uwb_est_find_size); diff --git a/drivers/staging/uwb/hwa-rc.c b/drivers/staging/uwb/hwa-rc.c deleted file mode 100644 index b6effad749d7..000000000000 --- a/drivers/staging/uwb/hwa-rc.c +++ /dev/null @@ -1,929 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * WUSB Host Wire Adapter: Radio Control Interface (WUSB[8.6]) - * Radio Control command/event transport - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * Initialize the Radio Control interface Driver. - * - * For each device probed, creates an 'struct hwarc' which contains - * just the representation of the UWB Radio Controller, and the logic - * for reading notifications and passing them to the UWB Core. - * - * So we initialize all of those, register the UWB Radio Controller - * and setup the notification/event handle to pipe the notifications - * to the UWB management Daemon. - * - * Command and event filtering. - * - * This is the driver for the Radio Control Interface described in WUSB - * 1.0. The core UWB module assumes that all drivers are compliant to the - * WHCI 0.95 specification. We thus create a filter that parses all - * incoming messages from the (WUSB 1.0) device and manipulate them to - * conform to the WHCI 0.95 specification. Similarly, outgoing messages - * are parsed and manipulated to conform to the WUSB 1.0 compliant messages - * that the device expects. Only a few messages are affected: - * Affected events: - * UWB_RC_EVT_BEACON - * UWB_RC_EVT_BP_SLOT_CHANGE - * UWB_RC_EVT_DRP_AVAIL - * UWB_RC_EVT_DRP - * Affected commands: - * UWB_RC_CMD_SCAN - * UWB_RC_CMD_SET_DRP_IE - */ -#include -#include -#include -#include -#include "../wusbcore/include/wusb.h" -#include "../wusbcore/include/wusb-wa.h" -#include "uwb.h" - -#include "uwb-internal.h" - -/* The device uses commands and events from the WHCI specification, although - * reporting itself as WUSB compliant. */ -#define WUSB_QUIRK_WHCI_CMD_EVT 0x01 - -/** - * Descriptor for an instance of the UWB Radio Control Driver that - * attaches to the RCI interface of the Host Wired Adapter. - * - * Unless there is a lock specific to the 'data members', all access - * is protected by uwb_rc->mutex. - * - * The NEEP (Notification/Event EndPoint) URB (@neep_urb) writes to - * @rd_buffer. Note there is no locking because it is perfectly (heh!) - * serialized--probe() submits an URB, callback is called, processes - * the data (synchronously), submits another URB, and so on. There is - * no concurrent access to the buffer. - */ -struct hwarc { - struct usb_device *usb_dev; - struct usb_interface *usb_iface; - struct uwb_rc *uwb_rc; /* UWB host controller */ - struct urb *neep_urb; /* Notification endpoint handling */ - struct edc neep_edc; - void *rd_buffer; /* NEEP read buffer */ -}; - - -/* Beacon received notification (WUSB 1.0 [8.6.3.2]) */ -struct uwb_rc_evt_beacon_WUSB_0100 { - struct uwb_rceb rceb; - u8 bChannelNumber; - __le16 wBPSTOffset; - u8 bLQI; - u8 bRSSI; - __le16 wBeaconInfoLength; - u8 BeaconInfo[]; -} __attribute__((packed)); - -/** - * Filter WUSB 1.0 BEACON RCV notification to be WHCI 0.95 - * - * @header: the incoming event - * @buf_size: size of buffer containing incoming event - * @new_size: size of event after filtering completed - * - * The WHCI 0.95 spec has a "Beacon Type" field. This value is unknown at - * the time we receive the beacon from WUSB so we just set it to - * UWB_RC_BEACON_TYPE_NEIGHBOR as a default. - * The solution below allocates memory upon receipt of every beacon from a - * WUSB device. This will deteriorate performance. What is the right way to - * do this? - */ -static -int hwarc_filter_evt_beacon_WUSB_0100(struct uwb_rc *rc, - struct uwb_rceb **header, - const size_t buf_size, - size_t *new_size) -{ - struct uwb_rc_evt_beacon_WUSB_0100 *be; - struct uwb_rc_evt_beacon *newbe; - size_t bytes_left, ielength; - struct device *dev = &rc->uwb_dev.dev; - - be = container_of(*header, struct uwb_rc_evt_beacon_WUSB_0100, rceb); - bytes_left = buf_size; - if (bytes_left < sizeof(*be)) { - dev_err(dev, "Beacon Received Notification: Not enough data " - "to decode for filtering (%zu vs %zu bytes needed)\n", - bytes_left, sizeof(*be)); - return -EINVAL; - } - bytes_left -= sizeof(*be); - ielength = le16_to_cpu(be->wBeaconInfoLength); - if (bytes_left < ielength) { - dev_err(dev, "Beacon Received Notification: Not enough data " - "to decode IEs (%zu vs %zu bytes needed)\n", - bytes_left, ielength); - return -EINVAL; - } - newbe = kzalloc(sizeof(*newbe) + ielength, GFP_ATOMIC); - if (newbe == NULL) - return -ENOMEM; - newbe->rceb = be->rceb; - newbe->bChannelNumber = be->bChannelNumber; - newbe->bBeaconType = UWB_RC_BEACON_TYPE_NEIGHBOR; - newbe->wBPSTOffset = be->wBPSTOffset; - newbe->bLQI = be->bLQI; - newbe->bRSSI = be->bRSSI; - newbe->wBeaconInfoLength = be->wBeaconInfoLength; - memcpy(newbe->BeaconInfo, be->BeaconInfo, ielength); - *header = &newbe->rceb; - *new_size = sizeof(*newbe) + ielength; - return 1; /* calling function will free memory */ -} - - -/* DRP Availability change notification (WUSB 1.0 [8.6.3.8]) */ -struct uwb_rc_evt_drp_avail_WUSB_0100 { - struct uwb_rceb rceb; - __le16 wIELength; - u8 IEData[]; -} __attribute__((packed)); - -/** - * Filter WUSB 1.0 DRP AVAILABILITY CHANGE notification to be WHCI 0.95 - * - * @header: the incoming event - * @buf_size: size of buffer containing incoming event - * @new_size: size of event after filtering completed - */ -static -int hwarc_filter_evt_drp_avail_WUSB_0100(struct uwb_rc *rc, - struct uwb_rceb **header, - const size_t buf_size, - size_t *new_size) -{ - struct uwb_rc_evt_drp_avail_WUSB_0100 *da; - struct uwb_rc_evt_drp_avail *newda; - struct uwb_ie_hdr *ie_hdr; - size_t bytes_left, ielength; - struct device *dev = &rc->uwb_dev.dev; - - - da = container_of(*header, struct uwb_rc_evt_drp_avail_WUSB_0100, rceb); - bytes_left = buf_size; - if (bytes_left < sizeof(*da)) { - dev_err(dev, "Not enough data to decode DRP Avail " - "Notification for filtering. Expected %zu, " - "received %zu.\n", (size_t)sizeof(*da), bytes_left); - return -EINVAL; - } - bytes_left -= sizeof(*da); - ielength = le16_to_cpu(da->wIELength); - if (bytes_left < ielength) { - dev_err(dev, "DRP Avail Notification filter: IE length " - "[%zu bytes] does not match actual length " - "[%zu bytes].\n", ielength, bytes_left); - return -EINVAL; - } - if (ielength < sizeof(*ie_hdr)) { - dev_err(dev, "DRP Avail Notification filter: Not enough " - "data to decode IE [%zu bytes, %zu needed]\n", - ielength, sizeof(*ie_hdr)); - return -EINVAL; - } - ie_hdr = (void *) da->IEData; - if (ie_hdr->length > 32) { - dev_err(dev, "DRP Availability Change event has unexpected " - "length for filtering. Expected < 32 bytes, " - "got %zu bytes.\n", (size_t)ie_hdr->length); - return -EINVAL; - } - newda = kzalloc(sizeof(*newda), GFP_ATOMIC); - if (newda == NULL) - return -ENOMEM; - newda->rceb = da->rceb; - memcpy(newda->bmp, (u8 *) ie_hdr + sizeof(*ie_hdr), ie_hdr->length); - *header = &newda->rceb; - *new_size = sizeof(*newda); - return 1; /* calling function will free memory */ -} - - -/* DRP notification (WUSB 1.0 [8.6.3.9]) */ -struct uwb_rc_evt_drp_WUSB_0100 { - struct uwb_rceb rceb; - struct uwb_dev_addr wSrcAddr; - u8 bExplicit; - __le16 wIELength; - u8 IEData[]; -} __attribute__((packed)); - -/** - * Filter WUSB 1.0 DRP Notification to be WHCI 0.95 - * - * @header: the incoming event - * @buf_size: size of buffer containing incoming event - * @new_size: size of event after filtering completed - * - * It is hard to manage DRP reservations without having a Reason code. - * Unfortunately there is none in the WUSB spec. We just set the default to - * DRP IE RECEIVED. - * We do not currently use the bBeaconSlotNumber value, so we set this to - * zero for now. - */ -static -int hwarc_filter_evt_drp_WUSB_0100(struct uwb_rc *rc, - struct uwb_rceb **header, - const size_t buf_size, - size_t *new_size) -{ - struct uwb_rc_evt_drp_WUSB_0100 *drpev; - struct uwb_rc_evt_drp *newdrpev; - size_t bytes_left, ielength; - struct device *dev = &rc->uwb_dev.dev; - - drpev = container_of(*header, struct uwb_rc_evt_drp_WUSB_0100, rceb); - bytes_left = buf_size; - if (bytes_left < sizeof(*drpev)) { - dev_err(dev, "Not enough data to decode DRP Notification " - "for filtering. Expected %zu, received %zu.\n", - (size_t)sizeof(*drpev), bytes_left); - return -EINVAL; - } - ielength = le16_to_cpu(drpev->wIELength); - bytes_left -= sizeof(*drpev); - if (bytes_left < ielength) { - dev_err(dev, "DRP Notification filter: header length [%zu " - "bytes] does not match actual length [%zu " - "bytes].\n", ielength, bytes_left); - return -EINVAL; - } - newdrpev = kzalloc(sizeof(*newdrpev) + ielength, GFP_ATOMIC); - if (newdrpev == NULL) - return -ENOMEM; - newdrpev->rceb = drpev->rceb; - newdrpev->src_addr = drpev->wSrcAddr; - newdrpev->reason = UWB_DRP_NOTIF_DRP_IE_RCVD; - newdrpev->beacon_slot_number = 0; - newdrpev->ie_length = drpev->wIELength; - memcpy(newdrpev->ie_data, drpev->IEData, ielength); - *header = &newdrpev->rceb; - *new_size = sizeof(*newdrpev) + ielength; - return 1; /* calling function will free memory */ -} - - -/* Scan Command (WUSB 1.0 [8.6.2.5]) */ -struct uwb_rc_cmd_scan_WUSB_0100 { - struct uwb_rccb rccb; - u8 bChannelNumber; - u8 bScanState; -} __attribute__((packed)); - -/** - * Filter WHCI 0.95 SCAN command to be WUSB 1.0 SCAN command - * - * @header: command sent to device (compliant to WHCI 0.95) - * @size: size of command sent to device - * - * We only reduce the size by two bytes because the WUSB 1.0 scan command - * does not have the last field (wStarttime). Also, make sure we don't send - * the device an unexpected scan type. - */ -static -int hwarc_filter_cmd_scan_WUSB_0100(struct uwb_rc *rc, - struct uwb_rccb **header, - size_t *size) -{ - struct uwb_rc_cmd_scan *sc; - - sc = container_of(*header, struct uwb_rc_cmd_scan, rccb); - - if (sc->bScanState == UWB_SCAN_ONLY_STARTTIME) - sc->bScanState = UWB_SCAN_ONLY; - /* Don't send the last two bytes. */ - *size -= 2; - return 0; -} - - -/* SET DRP IE command (WUSB 1.0 [8.6.2.7]) */ -struct uwb_rc_cmd_set_drp_ie_WUSB_0100 { - struct uwb_rccb rccb; - u8 bExplicit; - __le16 wIELength; - struct uwb_ie_drp IEData[]; -} __attribute__((packed)); - -/** - * Filter WHCI 0.95 SET DRP IE command to be WUSB 1.0 SET DRP IE command - * - * @header: command sent to device (compliant to WHCI 0.95) - * @size: size of command sent to device - * - * WUSB has an extra bExplicit field - we assume always explicit - * negotiation so this field is set. The command expected by the device is - * thus larger than the one prepared by the driver so we need to - * reallocate memory to accommodate this. - * We trust the driver to send us the correct data so no checking is done - * on incoming data - evn though it is variable length. - */ -static -int hwarc_filter_cmd_set_drp_ie_WUSB_0100(struct uwb_rc *rc, - struct uwb_rccb **header, - size_t *size) -{ - struct uwb_rc_cmd_set_drp_ie *orgcmd; - struct uwb_rc_cmd_set_drp_ie_WUSB_0100 *cmd; - size_t ielength; - - orgcmd = container_of(*header, struct uwb_rc_cmd_set_drp_ie, rccb); - ielength = le16_to_cpu(orgcmd->wIELength); - cmd = kzalloc(sizeof(*cmd) + ielength, GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - cmd->rccb = orgcmd->rccb; - cmd->bExplicit = 0; - cmd->wIELength = orgcmd->wIELength; - memcpy(cmd->IEData, orgcmd->IEData, ielength); - *header = &cmd->rccb; - *size = sizeof(*cmd) + ielength; - return 1; /* calling function will free memory */ -} - - -/** - * Filter data from WHCI driver to WUSB device - * - * @header: WHCI 0.95 compliant command from driver - * @size: length of command - * - * The routine managing commands to the device (uwb_rc_cmd()) will call the - * filtering function pointer (if it exists) before it passes any data to - * the device. At this time the command has been formatted according to - * WHCI 0.95 and is ready to be sent to the device. - * - * The filter function will be provided with the current command and its - * length. The function will manipulate the command if necessary and - * potentially reallocate memory for a command that needed more memory that - * the given command. If new memory was created the function will return 1 - * to indicate to the calling function that the memory need to be freed - * when not needed any more. The size will contain the new length of the - * command. - * If memory has not been allocated we rely on the original mechanisms to - * free the memory of the command - even when we reduce the value of size. - */ -static -int hwarc_filter_cmd_WUSB_0100(struct uwb_rc *rc, struct uwb_rccb **header, - size_t *size) -{ - int result; - struct uwb_rccb *rccb = *header; - int cmd = le16_to_cpu(rccb->wCommand); - switch (cmd) { - case UWB_RC_CMD_SCAN: - result = hwarc_filter_cmd_scan_WUSB_0100(rc, header, size); - break; - case UWB_RC_CMD_SET_DRP_IE: - result = hwarc_filter_cmd_set_drp_ie_WUSB_0100(rc, header, size); - break; - default: - result = -ENOANO; - break; - } - return result; -} - - -/** - * Filter data from WHCI driver to WUSB device - * - * @header: WHCI 0.95 compliant command from driver - * @size: length of command - * - * Filter commands based on which protocol the device supports. The WUSB - * errata should be the same as WHCI 0.95 so we do not filter that here - - * only WUSB 1.0. - */ -static -int hwarc_filter_cmd(struct uwb_rc *rc, struct uwb_rccb **header, - size_t *size) -{ - int result = -ENOANO; - if (rc->version == 0x0100) - result = hwarc_filter_cmd_WUSB_0100(rc, header, size); - return result; -} - - -/** - * Compute return value as sum of incoming value and value at given offset - * - * @rceb: event for which we compute the size, it contains a variable - * length field. - * @core_size: size of the "non variable" part of the event - * @offset: place in event where the length of the variable part is stored - * @buf_size: total length of buffer in which event arrived - we need to make - * sure we read the offset in memory that is still part of the event - */ -static -ssize_t hwarc_get_event_size(struct uwb_rc *rc, const struct uwb_rceb *rceb, - size_t core_size, size_t offset, - const size_t buf_size) -{ - ssize_t size = -ENOSPC; - const void *ptr = rceb; - size_t type_size = sizeof(__le16); - struct device *dev = &rc->uwb_dev.dev; - - if (offset + type_size >= buf_size) { - dev_err(dev, "Not enough data to read extra size of event " - "0x%02x/%04x/%02x, only got %zu bytes.\n", - rceb->bEventType, le16_to_cpu(rceb->wEvent), - rceb->bEventContext, buf_size); - goto out; - } - ptr += offset; - size = core_size + le16_to_cpu(*(__le16 *)ptr); -out: - return size; -} - - -/* Beacon slot change notification (WUSB 1.0 [8.6.3.5]) */ -struct uwb_rc_evt_bp_slot_change_WUSB_0100 { - struct uwb_rceb rceb; - u8 bSlotNumber; -} __attribute__((packed)); - - -/** - * Filter data from WUSB device to WHCI driver - * - * @header: incoming event - * @buf_size: size of buffer in which event arrived - * @_event_size: actual size of event in the buffer - * @new_size: size of event after filtered - * - * We don't know how the buffer is constructed - there may be more than one - * event in it so buffer length does not determine event length. We first - * determine the expected size of the incoming event. This value is passed - * back only if the actual filtering succeeded (so we know the computed - * expected size is correct). This value will be zero if - * the event did not need any filtering. - * - * WHCI interprets the BP Slot Change event's data differently than - * WUSB. The event sizes are exactly the same. The data field - * indicates the new beacon slot in which a RC is transmitting its - * beacon. The maximum value of this is 96 (wMacBPLength ECMA-368 - * 17.16 (Table 117)). We thus know that the WUSB value will not set - * the bit bNoSlot, so we don't really do anything (placeholder). - */ -static -int hwarc_filter_event_WUSB_0100(struct uwb_rc *rc, struct uwb_rceb **header, - const size_t buf_size, size_t *_real_size, - size_t *_new_size) -{ - int result = -ENOANO; - struct uwb_rceb *rceb = *header; - int event = le16_to_cpu(rceb->wEvent); - ssize_t event_size; - size_t core_size, offset; - - if (rceb->bEventType != UWB_RC_CET_GENERAL) - goto out; - switch (event) { - case UWB_RC_EVT_BEACON: - core_size = sizeof(struct uwb_rc_evt_beacon_WUSB_0100); - offset = offsetof(struct uwb_rc_evt_beacon_WUSB_0100, - wBeaconInfoLength); - event_size = hwarc_get_event_size(rc, rceb, core_size, - offset, buf_size); - if (event_size < 0) - goto out; - *_real_size = event_size; - result = hwarc_filter_evt_beacon_WUSB_0100(rc, header, - buf_size, _new_size); - break; - case UWB_RC_EVT_BP_SLOT_CHANGE: - *_new_size = *_real_size = - sizeof(struct uwb_rc_evt_bp_slot_change_WUSB_0100); - result = 0; - break; - - case UWB_RC_EVT_DRP_AVAIL: - core_size = sizeof(struct uwb_rc_evt_drp_avail_WUSB_0100); - offset = offsetof(struct uwb_rc_evt_drp_avail_WUSB_0100, - wIELength); - event_size = hwarc_get_event_size(rc, rceb, core_size, - offset, buf_size); - if (event_size < 0) - goto out; - *_real_size = event_size; - result = hwarc_filter_evt_drp_avail_WUSB_0100( - rc, header, buf_size, _new_size); - break; - - case UWB_RC_EVT_DRP: - core_size = sizeof(struct uwb_rc_evt_drp_WUSB_0100); - offset = offsetof(struct uwb_rc_evt_drp_WUSB_0100, wIELength); - event_size = hwarc_get_event_size(rc, rceb, core_size, - offset, buf_size); - if (event_size < 0) - goto out; - *_real_size = event_size; - result = hwarc_filter_evt_drp_WUSB_0100(rc, header, - buf_size, _new_size); - break; - - default: - break; - } -out: - return result; -} - -/** - * Filter data from WUSB device to WHCI driver - * - * @header: incoming event - * @buf_size: size of buffer in which event arrived - * @_event_size: actual size of event in the buffer - * @_new_size: size of event after filtered - * - * Filter events based on which protocol the device supports. The WUSB - * errata should be the same as WHCI 0.95 so we do not filter that here - - * only WUSB 1.0. - * - * If we don't handle it, we return -ENOANO (why the weird error code? - * well, so if I get it, I can pinpoint in the code that raised - * it...after all, not too many places use the higher error codes). - */ -static -int hwarc_filter_event(struct uwb_rc *rc, struct uwb_rceb **header, - const size_t buf_size, size_t *_real_size, - size_t *_new_size) -{ - int result = -ENOANO; - if (rc->version == 0x0100) - result = hwarc_filter_event_WUSB_0100( - rc, header, buf_size, _real_size, _new_size); - return result; -} - - -/** - * Execute an UWB RC command on HWA - * - * @rc: Instance of a Radio Controller that is a HWA - * @cmd: Buffer containing the RCCB and payload to execute - * @cmd_size: Size of the command buffer. - * - * NOTE: rc's mutex has to be locked - */ -static -int hwarc_cmd(struct uwb_rc *uwb_rc, const struct uwb_rccb *cmd, size_t cmd_size) -{ - struct hwarc *hwarc = uwb_rc->priv; - return usb_control_msg( - hwarc->usb_dev, usb_sndctrlpipe(hwarc->usb_dev, 0), - WA_EXEC_RC_CMD, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, hwarc->usb_iface->cur_altsetting->desc.bInterfaceNumber, - (void *) cmd, cmd_size, 100 /* FIXME: this is totally arbitrary */); -} - -static -int hwarc_reset(struct uwb_rc *uwb_rc) -{ - struct hwarc *hwarc = uwb_rc->priv; - int result; - - /* device lock must be held when calling usb_reset_device. */ - result = usb_lock_device_for_reset(hwarc->usb_dev, NULL); - if (result >= 0) { - result = usb_reset_device(hwarc->usb_dev); - usb_unlock_device(hwarc->usb_dev); - } - - return result; -} - -/** - * Callback for the notification and event endpoint - * - * Check's that everything is fine and then passes the read data to - * the notification/event handling mechanism (neh). - */ -static -void hwarc_neep_cb(struct urb *urb) -{ - struct hwarc *hwarc = urb->context; - struct usb_interface *usb_iface = hwarc->usb_iface; - struct device *dev = &usb_iface->dev; - int result; - - switch (result = urb->status) { - case 0: - uwb_rc_neh_grok(hwarc->uwb_rc, urb->transfer_buffer, - urb->actual_length); - break; - case -ECONNRESET: /* Not an error, but a controlled situation; */ - case -ENOENT: /* (we killed the URB)...so, no broadcast */ - goto out; - case -ESHUTDOWN: /* going away! */ - goto out; - default: /* On general errors, retry unless it gets ugly */ - if (edc_inc(&hwarc->neep_edc, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)) - goto error_exceeded; - dev_err(dev, "NEEP: URB error %d\n", urb->status); - } - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result < 0 && result != -ENODEV && result != -EPERM) { - /* ignoring unrecoverable errors */ - dev_err(dev, "NEEP: Can't resubmit URB (%d) resetting device\n", - result); - goto error; - } -out: - return; - -error_exceeded: - dev_err(dev, "NEEP: URB max acceptable errors " - "exceeded, resetting device\n"); -error: - uwb_rc_neh_error(hwarc->uwb_rc, result); - uwb_rc_reset_all(hwarc->uwb_rc); - return; -} - -static void hwarc_init(struct hwarc *hwarc) -{ - edc_init(&hwarc->neep_edc); -} - -/** - * Initialize the notification/event endpoint stuff - * - * Note this is effectively a parallel thread; it knows that - * hwarc->uwb_rc always exists because the existence of a 'hwarc' - * means that there is a reverence on the hwarc->uwb_rc (see - * _probe()), and thus _neep_cb() can execute safely. - */ -static int hwarc_neep_init(struct uwb_rc *rc) -{ - struct hwarc *hwarc = rc->priv; - struct usb_interface *iface = hwarc->usb_iface; - struct usb_device *usb_dev = interface_to_usbdev(iface); - struct device *dev = &iface->dev; - int result; - struct usb_endpoint_descriptor *epd; - - epd = &iface->cur_altsetting->endpoint[0].desc; - hwarc->rd_buffer = (void *) __get_free_page(GFP_KERNEL); - if (hwarc->rd_buffer == NULL) { - dev_err(dev, "Unable to allocate notification's read buffer\n"); - goto error_rd_buffer; - } - hwarc->neep_urb = usb_alloc_urb(0, GFP_KERNEL); - if (hwarc->neep_urb == NULL) - goto error_urb_alloc; - usb_fill_int_urb(hwarc->neep_urb, usb_dev, - usb_rcvintpipe(usb_dev, epd->bEndpointAddress), - hwarc->rd_buffer, PAGE_SIZE, - hwarc_neep_cb, hwarc, epd->bInterval); - result = usb_submit_urb(hwarc->neep_urb, GFP_ATOMIC); - if (result < 0) { - dev_err(dev, "Cannot submit notification URB: %d\n", result); - goto error_neep_submit; - } - return 0; - -error_neep_submit: - usb_free_urb(hwarc->neep_urb); - hwarc->neep_urb = NULL; -error_urb_alloc: - free_page((unsigned long)hwarc->rd_buffer); - hwarc->rd_buffer = NULL; -error_rd_buffer: - return -ENOMEM; -} - - -/** Clean up all the notification endpoint resources */ -static void hwarc_neep_release(struct uwb_rc *rc) -{ - struct hwarc *hwarc = rc->priv; - - usb_kill_urb(hwarc->neep_urb); - usb_free_urb(hwarc->neep_urb); - hwarc->neep_urb = NULL; - - free_page((unsigned long)hwarc->rd_buffer); - hwarc->rd_buffer = NULL; -} - -/** - * Get the version from class-specific descriptor - * - * NOTE: this descriptor comes with the big bundled configuration - * descriptor that includes the interfaces' and endpoints', so - * we just look for it in the cached copy kept by the USB stack. - * - * NOTE2: We convert LE fields to CPU order. - */ -static int hwarc_get_version(struct uwb_rc *rc) -{ - int result; - - struct hwarc *hwarc = rc->priv; - struct uwb_rc_control_intf_class_desc *descr; - struct device *dev = &rc->uwb_dev.dev; - struct usb_device *usb_dev = hwarc->usb_dev; - char *itr; - struct usb_descriptor_header *hdr; - size_t itr_size, actconfig_idx; - u16 version; - - actconfig_idx = (usb_dev->actconfig - usb_dev->config) / - sizeof(usb_dev->config[0]); - itr = usb_dev->rawdescriptors[actconfig_idx]; - itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength); - while (itr_size >= sizeof(*hdr)) { - hdr = (struct usb_descriptor_header *) itr; - dev_dbg(dev, "Extra device descriptor: " - "type %02x/%u bytes @ %zu (%zu left)\n", - hdr->bDescriptorType, hdr->bLength, - (itr - usb_dev->rawdescriptors[actconfig_idx]), - itr_size); - if (hdr->bDescriptorType == USB_DT_CS_RADIO_CONTROL) - goto found; - itr += hdr->bLength; - itr_size -= hdr->bLength; - } - dev_err(dev, "cannot find Radio Control Interface Class descriptor\n"); - return -ENODEV; - -found: - result = -EINVAL; - if (hdr->bLength > itr_size) { /* is it available? */ - dev_err(dev, "incomplete Radio Control Interface Class " - "descriptor (%zu bytes left, %u needed)\n", - itr_size, hdr->bLength); - goto error; - } - if (hdr->bLength < sizeof(*descr)) { - dev_err(dev, "short Radio Control Interface Class " - "descriptor\n"); - goto error; - } - descr = (struct uwb_rc_control_intf_class_desc *) hdr; - /* Make LE fields CPU order */ - version = __le16_to_cpu(descr->bcdRCIVersion); - if (version != 0x0100) { - dev_err(dev, "Device reports protocol version 0x%04x. We " - "do not support that. \n", version); - result = -EINVAL; - goto error; - } - rc->version = version; - dev_dbg(dev, "Device supports WUSB protocol version 0x%04x \n", rc->version); - result = 0; -error: - return result; -} - -/* - * By creating a 'uwb_rc', we have a reference on it -- that reference - * is the one we drop when we disconnect. - * - * No need to switch altsettings; according to WUSB1.0[8.6.1.1], there - * is only one altsetting allowed. - */ -static int hwarc_probe(struct usb_interface *iface, - const struct usb_device_id *id) -{ - int result; - struct uwb_rc *uwb_rc; - struct hwarc *hwarc; - struct device *dev = &iface->dev; - - if (iface->cur_altsetting->desc.bNumEndpoints < 1) - return -ENODEV; - if (!usb_endpoint_xfer_int(&iface->cur_altsetting->endpoint[0].desc)) - return -ENODEV; - - result = -ENOMEM; - uwb_rc = uwb_rc_alloc(); - if (uwb_rc == NULL) { - dev_err(dev, "unable to allocate RC instance\n"); - goto error_rc_alloc; - } - hwarc = kzalloc(sizeof(*hwarc), GFP_KERNEL); - if (hwarc == NULL) { - dev_err(dev, "unable to allocate HWA RC instance\n"); - goto error_alloc; - } - hwarc_init(hwarc); - hwarc->usb_dev = usb_get_dev(interface_to_usbdev(iface)); - hwarc->usb_iface = usb_get_intf(iface); - hwarc->uwb_rc = uwb_rc; - - uwb_rc->owner = THIS_MODULE; - uwb_rc->start = hwarc_neep_init; - uwb_rc->stop = hwarc_neep_release; - uwb_rc->cmd = hwarc_cmd; - uwb_rc->reset = hwarc_reset; - if (id->driver_info & WUSB_QUIRK_WHCI_CMD_EVT) { - uwb_rc->filter_cmd = NULL; - uwb_rc->filter_event = NULL; - } else { - uwb_rc->filter_cmd = hwarc_filter_cmd; - uwb_rc->filter_event = hwarc_filter_event; - } - - result = uwb_rc_add(uwb_rc, dev, hwarc); - if (result < 0) - goto error_rc_add; - result = hwarc_get_version(uwb_rc); - if (result < 0) { - dev_err(dev, "cannot retrieve version of RC \n"); - goto error_get_version; - } - usb_set_intfdata(iface, hwarc); - return 0; - -error_get_version: - uwb_rc_rm(uwb_rc); -error_rc_add: - usb_put_intf(iface); - usb_put_dev(hwarc->usb_dev); - kfree(hwarc); -error_alloc: - uwb_rc_put(uwb_rc); -error_rc_alloc: - return result; -} - -static void hwarc_disconnect(struct usb_interface *iface) -{ - struct hwarc *hwarc = usb_get_intfdata(iface); - struct uwb_rc *uwb_rc = hwarc->uwb_rc; - - usb_set_intfdata(hwarc->usb_iface, NULL); - uwb_rc_rm(uwb_rc); - usb_put_intf(hwarc->usb_iface); - usb_put_dev(hwarc->usb_dev); - kfree(hwarc); - uwb_rc_put(uwb_rc); /* when creating the device, refcount = 1 */ -} - -static int hwarc_pre_reset(struct usb_interface *iface) -{ - struct hwarc *hwarc = usb_get_intfdata(iface); - struct uwb_rc *uwb_rc = hwarc->uwb_rc; - - uwb_rc_pre_reset(uwb_rc); - return 0; -} - -static int hwarc_post_reset(struct usb_interface *iface) -{ - struct hwarc *hwarc = usb_get_intfdata(iface); - struct uwb_rc *uwb_rc = hwarc->uwb_rc; - - return uwb_rc_post_reset(uwb_rc); -} - -/** USB device ID's that we handle */ -static const struct usb_device_id hwarc_id_table[] = { - /* D-Link DUB-1210 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3d02, 0xe0, 0x01, 0x02), - .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, - /* Intel i1480 (using firmware 1.3PA2-20070828) */ - { USB_DEVICE_AND_INTERFACE_INFO(0x8086, 0x0c3b, 0xe0, 0x01, 0x02), - .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, - /* Alereon 5310 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x01, 0x02), - .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, - /* Alereon 5611 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x01, 0x02), - .driver_info = WUSB_QUIRK_WHCI_CMD_EVT }, - /* Generic match for the Radio Control interface */ - { USB_INTERFACE_INFO(0xe0, 0x01, 0x02), }, - { }, -}; -MODULE_DEVICE_TABLE(usb, hwarc_id_table); - -static struct usb_driver hwarc_driver = { - .name = "hwa-rc", - .id_table = hwarc_id_table, - .probe = hwarc_probe, - .disconnect = hwarc_disconnect, - .pre_reset = hwarc_pre_reset, - .post_reset = hwarc_post_reset, -}; - -module_usb_driver(hwarc_driver); - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("Host Wireless Adapter Radio Control Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/uwb/i1480/Makefile b/drivers/staging/uwb/i1480/Makefile deleted file mode 100644 index d26fb9b845ae..000000000000 --- a/drivers/staging/uwb/i1480/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o diff --git a/drivers/staging/uwb/i1480/dfu/Makefile b/drivers/staging/uwb/i1480/dfu/Makefile deleted file mode 100644 index 4739fdac5922..000000000000 --- a/drivers/staging/uwb/i1480/dfu/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_UWB_I1480U) += i1480-dfu-usb.o - -i1480-dfu-usb-objs := \ - dfu.o \ - mac.o \ - phy.o \ - usb.o - - diff --git a/drivers/staging/uwb/i1480/dfu/dfu.c b/drivers/staging/uwb/i1480/dfu/dfu.c deleted file mode 100644 index 9d51ce8faad1..000000000000 --- a/drivers/staging/uwb/i1480/dfu/dfu.c +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Wireless UWB Link 1480 - * Main driver - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * Common code for firmware upload used by the USB and PCI version; - * i1480_fw_upload() takes a device descriptor and uses the function - * pointers it provides to upload firmware and prepare the PHY. - * - * As well, provides common functions used by the rest of the code. - */ -#include "i1480-dfu.h" -#include -#include -#include -#include -#include -#include -#include "../../uwb.h" - -/* - * i1480_rceb_check - Check RCEB for expected field values - * @i1480: pointer to device for which RCEB is being checked - * @rceb: RCEB being checked - * @cmd: which command the RCEB is related to - * @context: expected context - * @expected_type: expected event type - * @expected_event: expected event - * - * If @cmd is NULL, do not print error messages, but still return an error - * code. - * - * Return 0 if @rceb matches the expected values, -EINVAL otherwise. - */ -int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb, - const char *cmd, u8 context, u8 expected_type, - unsigned expected_event) -{ - int result = 0; - struct device *dev = i1480->dev; - if (rceb->bEventContext != context) { - if (cmd) - dev_err(dev, "%s: unexpected context id 0x%02x " - "(expected 0x%02x)\n", cmd, - rceb->bEventContext, context); - result = -EINVAL; - } - if (rceb->bEventType != expected_type) { - if (cmd) - dev_err(dev, "%s: unexpected event type 0x%02x " - "(expected 0x%02x)\n", cmd, - rceb->bEventType, expected_type); - result = -EINVAL; - } - if (le16_to_cpu(rceb->wEvent) != expected_event) { - if (cmd) - dev_err(dev, "%s: unexpected event 0x%04x " - "(expected 0x%04x)\n", cmd, - le16_to_cpu(rceb->wEvent), expected_event); - result = -EINVAL; - } - return result; -} -EXPORT_SYMBOL_GPL(i1480_rceb_check); - - -/* - * Execute a Radio Control Command - * - * Command data has to be in i1480->cmd_buf. - * - * @returns size of the reply data filled in i1480->evt_buf or < 0 errno - * code on error. - */ -ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size, - size_t reply_size) -{ - ssize_t result; - struct uwb_rceb *reply = i1480->evt_buf; - struct uwb_rccb *cmd = i1480->cmd_buf; - u16 expected_event = reply->wEvent; - u8 expected_type = reply->bEventType; - u8 context; - - init_completion(&i1480->evt_complete); - i1480->evt_result = -EINPROGRESS; - do { - get_random_bytes(&context, 1); - } while (context == 0x00 || context == 0xff); - cmd->bCommandContext = context; - result = i1480->cmd(i1480, cmd_name, cmd_size); - if (result < 0) - goto error; - /* wait for the callback to report a event was received */ - result = wait_for_completion_interruptible_timeout( - &i1480->evt_complete, HZ); - if (result == 0) { - result = -ETIMEDOUT; - goto error; - } - if (result < 0) - goto error; - result = i1480->evt_result; - if (result < 0) { - dev_err(i1480->dev, "%s: command reply reception failed: %zd\n", - cmd_name, result); - goto error; - } - /* - * Firmware versions >= 1.4.12224 for IOGear GUWA100U generate a - * spurious notification after firmware is downloaded. So check whether - * the receibed RCEB is such notification before assuming that the - * command has failed. - */ - if (i1480_rceb_check(i1480, i1480->evt_buf, NULL, - 0, 0xfd, 0x0022) == 0) { - /* Now wait for the actual RCEB for this command. */ - result = i1480->wait_init_done(i1480); - if (result < 0) - goto error; - result = i1480->evt_result; - } - if (result != reply_size) { - dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n", - cmd_name, result, reply_size); - result = -EINVAL; - goto error; - } - /* Verify we got the right event in response */ - result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context, - expected_type, expected_event); -error: - return result; -} -EXPORT_SYMBOL_GPL(i1480_cmd); - - -static -int i1480_print_state(struct i1480 *i1480) -{ - int result; - u32 *buf = (u32 *) i1480->cmd_buf; - - result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf)); - if (result < 0) { - dev_err(i1480->dev, "cannot read U & L states: %d\n", result); - goto error; - } - dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]); -error: - return result; -} - - -/* - * PCI probe, firmware uploader - * - * _mac_fw_upload() will call rc_setup(), which needs an rc_release(). - */ -int i1480_fw_upload(struct i1480 *i1480) -{ - int result; - - result = i1480_pre_fw_upload(i1480); /* PHY pre fw */ - if (result < 0 && result != -ENOENT) { - i1480_print_state(i1480); - goto error; - } - result = i1480_mac_fw_upload(i1480); /* MAC fw */ - if (result < 0) { - if (result == -ENOENT) - dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n", - i1480->mac_fw_name); - else - i1480_print_state(i1480); - goto error; - } - result = i1480_phy_fw_upload(i1480); /* PHY fw */ - if (result < 0 && result != -ENOENT) { - i1480_print_state(i1480); - goto error_rc_release; - } - /* - * FIXME: find some reliable way to check whether firmware is running - * properly. Maybe use some standard request that has no side effects? - */ - dev_info(i1480->dev, "firmware uploaded successfully\n"); -error_rc_release: - if (i1480->rc_release) - i1480->rc_release(i1480); - result = 0; -error: - return result; -} -EXPORT_SYMBOL_GPL(i1480_fw_upload); diff --git a/drivers/staging/uwb/i1480/dfu/i1480-dfu.h b/drivers/staging/uwb/i1480/dfu/i1480-dfu.h deleted file mode 100644 index b21d058ecc23..000000000000 --- a/drivers/staging/uwb/i1480/dfu/i1480-dfu.h +++ /dev/null @@ -1,246 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * i1480 Device Firmware Upload - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This driver is the firmware uploader for the Intel Wireless UWB - * Link 1480 device (both in the USB and PCI incarnations). - * - * The process is quite simple: we stop the device, write the firmware - * to its memory and then restart it. Wait for the device to let us - * know it is done booting firmware. Ready. - * - * We might have to upload before or after a phy firmware (which might - * be done in two methods, using a normal firmware image or through - * the MPI port). - * - * Because USB and PCI use common methods, we just make ops out of the - * common operations (read, write, wait_init_done and cmd) and - * implement them in usb.c and pci.c. - * - * The flow is (some parts omitted): - * - * i1480_{usb,pci}_probe() On enumerate/discovery - * i1480_fw_upload() - * i1480_pre_fw_upload() - * __mac_fw_upload() - * fw_hdrs_load() - * mac_fw_hdrs_push() - * i1480->write() [i1480_{usb,pci}_write()] - * i1480_fw_cmp() - * i1480->read() [i1480_{usb,pci}_read()] - * i1480_mac_fw_upload() - * __mac_fw_upload() - * i1480->setup(() - * i1480->wait_init_done() - * i1480_cmd_reset() - * i1480->cmd() [i1480_{usb,pci}_cmd()] - * ... - * i1480_phy_fw_upload() - * request_firmware() - * i1480_mpi_write() - * i1480->cmd() [i1480_{usb,pci}_cmd()] - * - * Once the probe function enumerates the device and uploads the - * firmware, we just exit with -ENODEV, as we don't really want to - * attach to the device. - */ -#ifndef __i1480_DFU_H__ -#define __i1480_DFU_H__ - -#include -#include -#include "../../include/spec.h" - -#define i1480_FW_UPLOAD_MODE_MASK (cpu_to_le32(0x00000018)) - -#if i1480_FW > 0x00000302 -#define i1480_RCEB_EXTENDED -#endif - -struct uwb_rccb; -struct uwb_rceb; - -/* - * Common firmware upload handlers - * - * Normally you embed this struct in another one specific to your hw. - * - * @write Write to device's memory from buffer. - * @read Read from device's memory to i1480->evt_buf. - * @setup Setup device after basic firmware is uploaded - * @wait_init_done - * Wait for the device to send a notification saying init - * is done. - * @cmd FOP for issuing the command to the hardware. The - * command data is contained in i1480->cmd_buf and the size - * is supplied as an argument. The command replied is put - * in i1480->evt_buf and the size in i1480->evt_result (or if - * an error, a < 0 errno code). - * - * @cmd_buf Memory buffer used to send commands to the device. - * Allocated by the upper layers i1480_fw_upload(). - * Size has to be @buf_size. - * @evt_buf Memory buffer used to place the async notifications - * received by the hw. Allocated by the upper layers - * i1480_fw_upload(). - * Size has to be @buf_size. - * @cmd_complete - * Low level driver uses this to notify code waiting afor - * an event that the event has arrived and data is in - * i1480->evt_buf (and size/result in i1480->evt_result). - * @hw_rev - * Use this value to activate dfu code to support new revisions - * of hardware. i1480_init() sets this to a default value. - * It should be updated by the USB and PCI code. - */ -struct i1480 { - struct device *dev; - - int (*write)(struct i1480 *, u32 addr, const void *, size_t); - int (*read)(struct i1480 *, u32 addr, size_t); - int (*rc_setup)(struct i1480 *); - void (*rc_release)(struct i1480 *); - int (*wait_init_done)(struct i1480 *); - int (*cmd)(struct i1480 *, const char *cmd_name, size_t cmd_size); - const char *pre_fw_name; - const char *mac_fw_name; - const char *mac_fw_name_deprecate; /* FIXME: Will go away */ - const char *phy_fw_name; - u8 hw_rev; - - size_t buf_size; /* size of both evt_buf and cmd_buf */ - void *evt_buf, *cmd_buf; - ssize_t evt_result; - struct completion evt_complete; -}; - -static inline -void i1480_init(struct i1480 *i1480) -{ - i1480->hw_rev = 1; - init_completion(&i1480->evt_complete); -} - -extern int i1480_fw_upload(struct i1480 *); -extern int i1480_pre_fw_upload(struct i1480 *); -extern int i1480_mac_fw_upload(struct i1480 *); -extern int i1480_phy_fw_upload(struct i1480 *); -extern ssize_t i1480_cmd(struct i1480 *, const char *, size_t, size_t); -extern int i1480_rceb_check(const struct i1480 *, - const struct uwb_rceb *, const char *, u8, - u8, unsigned); - -enum { - /* Vendor specific command type */ - i1480_CET_VS1 = 0xfd, - /* i1480 commands */ - i1480_CMD_SET_IP_MAS = 0x000e, - i1480_CMD_GET_MAC_PHY_INFO = 0x0003, - i1480_CMD_MPI_WRITE = 0x000f, - i1480_CMD_MPI_READ = 0x0010, - /* i1480 events */ -#if i1480_FW > 0x00000302 - i1480_EVT_CONFIRM = 0x0002, - i1480_EVT_RM_INIT_DONE = 0x0101, - i1480_EVT_DEV_ADD = 0x0103, - i1480_EVT_DEV_RM = 0x0104, - i1480_EVT_DEV_ID_CHANGE = 0x0105, - i1480_EVT_GET_MAC_PHY_INFO = i1480_CMD_GET_MAC_PHY_INFO, -#else - i1480_EVT_CONFIRM = 0x0002, - i1480_EVT_RM_INIT_DONE = 0x0101, - i1480_EVT_DEV_ADD = 0x0103, - i1480_EVT_DEV_RM = 0x0104, - i1480_EVT_DEV_ID_CHANGE = 0x0105, - i1480_EVT_GET_MAC_PHY_INFO = i1480_EVT_CONFIRM, -#endif -}; - - -struct i1480_evt_confirm { - struct uwb_rceb rceb; -#ifdef i1480_RCEB_EXTENDED - __le16 wParamLength; -#endif - u8 bResultCode; -} __attribute__((packed)); - - -struct i1480_rceb { - struct uwb_rceb rceb; -#ifdef i1480_RCEB_EXTENDED - __le16 wParamLength; -#endif -} __attribute__((packed)); - - -/** - * Get MAC & PHY Information confirm event structure - * - * Confirm event returned by the command. - */ -struct i1480_evt_confirm_GMPI { -#if i1480_FW > 0x00000302 - struct uwb_rceb rceb; - __le16 wParamLength; - __le16 status; - u8 mac_addr[6]; /* EUI-64 bit IEEE address [still 8 bytes?] */ - u8 dev_addr[2]; - __le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */ - u8 hw_rev; - u8 phy_vendor; - u8 phy_rev; /* major v = >> 8; minor = v & 0xff */ - __le16 mac_caps; - u8 phy_caps[3]; - u8 key_stores; - __le16 mcast_addr_stores; - u8 sec_mode_supported; -#else - struct uwb_rceb rceb; - u8 status; - u8 mac_addr[8]; /* EUI-64 bit IEEE address [still 8 bytes?] */ - u8 dev_addr[2]; - __le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */ - __le16 phy_fw_rev; /* major v = >> 8; minor = v & 0xff */ - __le16 mac_caps; - u8 phy_caps; - u8 key_stores; - __le16 mcast_addr_stores; - u8 sec_mode_supported; -#endif -} __attribute__((packed)); - - -struct i1480_cmd_mpi_write { - struct uwb_rccb rccb; - __le16 size; - u8 data[]; -}; - - -struct i1480_cmd_mpi_read { - struct uwb_rccb rccb; - __le16 size; - struct { - u8 page, offset; - } __attribute__((packed)) data[]; -} __attribute__((packed)); - - -struct i1480_evt_mpi_read { - struct uwb_rceb rceb; -#ifdef i1480_RCEB_EXTENDED - __le16 wParamLength; -#endif - u8 bResultCode; - __le16 size; - struct { - u8 page, offset, value; - } __attribute__((packed)) data[]; -} __attribute__((packed)); - - -#endif /* #ifndef __i1480_DFU_H__ */ diff --git a/drivers/staging/uwb/i1480/dfu/mac.c b/drivers/staging/uwb/i1480/dfu/mac.c deleted file mode 100644 index 6e4d6c9cecf5..000000000000 --- a/drivers/staging/uwb/i1480/dfu/mac.c +++ /dev/null @@ -1,496 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Wireless UWB Link 1480 - * MAC Firmware upload implementation - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * Implementation of the code for parsing the firmware file (extract - * the headers and binary code chunks) in the fw_*() functions. The - * code to upload pre and mac firmwares is the same, so it uses a - * common entry point in __mac_fw_upload(), which uses the i1480 - * function pointers to push the firmware to the device. - */ -#include -#include -#include -#include "../../uwb.h" -#include "i1480-dfu.h" - -/* - * Descriptor for a continuous segment of MAC fw data - */ -struct fw_hdr { - unsigned long address; - size_t length; - const u32 *bin; - struct fw_hdr *next; -}; - - -/* Free a chain of firmware headers */ -static -void fw_hdrs_free(struct fw_hdr *hdr) -{ - struct fw_hdr *next; - - while (hdr) { - next = hdr->next; - kfree(hdr); - hdr = next; - } -} - - -/* Fill a firmware header descriptor from a memory buffer */ -static -int fw_hdr_load(struct i1480 *i1480, struct fw_hdr *hdr, unsigned hdr_cnt, - const char *_data, const u32 *data_itr, const u32 *data_top) -{ - size_t hdr_offset = (const char *) data_itr - _data; - size_t remaining_size = (void *) data_top - (void *) data_itr; - if (data_itr + 2 > data_top) { - dev_err(i1480->dev, "fw hdr #%u/%zu: EOF reached in header at " - "offset %zu, limit %zu\n", - hdr_cnt, hdr_offset, - (const char *) data_itr + 2 - _data, - (const char *) data_top - _data); - return -EINVAL; - } - hdr->next = NULL; - hdr->address = le32_to_cpu(*data_itr++); - hdr->length = le32_to_cpu(*data_itr++); - hdr->bin = data_itr; - if (hdr->length > remaining_size) { - dev_err(i1480->dev, "fw hdr #%u/%zu: EOF reached in data; " - "chunk too long (%zu bytes), only %zu left\n", - hdr_cnt, hdr_offset, hdr->length, remaining_size); - return -EINVAL; - } - return 0; -} - - -/** - * Get a buffer where the firmware is supposed to be and create a - * chain of headers linking them together. - * - * @phdr: where to place the pointer to the first header (headers link - * to the next via the @hdr->next ptr); need to free the whole - * chain when done. - * - * @_data: Pointer to the data buffer. - * - * @_data_size: Size of the data buffer (bytes); data size has to be a - * multiple of 4. Function will fail if not. - * - * Goes over the whole binary blob; reads the first chunk and creates - * a fw hdr from it (which points to where the data is in @_data and - * the length of the chunk); then goes on to the next chunk until - * done. Each header is linked to the next. - */ -static -int fw_hdrs_load(struct i1480 *i1480, struct fw_hdr **phdr, - const char *_data, size_t data_size) -{ - int result; - unsigned hdr_cnt = 0; - u32 *data = (u32 *) _data, *data_itr, *data_top; - struct fw_hdr *hdr, **prev_hdr = phdr; - - result = -EINVAL; - /* Check size is ok and pointer is aligned */ - if (data_size % sizeof(u32) != 0) - goto error; - if ((unsigned long) _data % sizeof(u16) != 0) - goto error; - *phdr = NULL; - data_itr = data; - data_top = (u32 *) (_data + data_size); - while (data_itr < data_top) { - result = -ENOMEM; - hdr = kmalloc(sizeof(*hdr), GFP_KERNEL); - if (hdr == NULL) { - dev_err(i1480->dev, "Cannot allocate fw header " - "for chunk #%u\n", hdr_cnt); - goto error_alloc; - } - result = fw_hdr_load(i1480, hdr, hdr_cnt, - _data, data_itr, data_top); - if (result < 0) - goto error_load; - data_itr += 2 + hdr->length; - *prev_hdr = hdr; - prev_hdr = &hdr->next; - hdr_cnt++; - }; - *prev_hdr = NULL; - return 0; - -error_load: - kfree(hdr); -error_alloc: - fw_hdrs_free(*phdr); -error: - return result; -} - - -/** - * Compares a chunk of fw with one in the devices's memory - * - * @i1480: Device instance - * @hdr: Pointer to the firmware chunk - * @returns: 0 if equal, < 0 errno on error. If > 0, it is the offset - * where the difference was found (plus one). - * - * Kind of dirty and simplistic, but does the trick in both the PCI - * and USB version. We do a quick[er] memcmp(), and if it fails, we do - * a byte-by-byte to find the offset. - */ -static -ssize_t i1480_fw_cmp(struct i1480 *i1480, struct fw_hdr *hdr) -{ - ssize_t result = 0; - u32 src_itr = 0, cnt; - size_t size = hdr->length*sizeof(hdr->bin[0]); - size_t chunk_size; - u8 *bin = (u8 *) hdr->bin; - - while (size > 0) { - chunk_size = size < i1480->buf_size ? size : i1480->buf_size; - result = i1480->read(i1480, hdr->address + src_itr, chunk_size); - if (result < 0) { - dev_err(i1480->dev, "error reading for verification: " - "%zd\n", result); - goto error; - } - if (memcmp(i1480->cmd_buf, bin + src_itr, result)) { - u8 *buf = i1480->cmd_buf; - for (cnt = 0; cnt < result; cnt++) - if (bin[src_itr + cnt] != buf[cnt]) { - dev_err(i1480->dev, "byte failed at " - "src_itr %u cnt %u [0x%02x " - "vs 0x%02x]\n", src_itr, cnt, - bin[src_itr + cnt], buf[cnt]); - result = src_itr + cnt + 1; - goto cmp_failed; - } - } - src_itr += result; - size -= result; - } - result = 0; -error: -cmp_failed: - return result; -} - - -/** - * Writes firmware headers to the device. - * - * @prd: PRD instance - * @hdr: Processed firmware - * @returns: 0 if ok, < 0 errno on error. - */ -static -int mac_fw_hdrs_push(struct i1480 *i1480, struct fw_hdr *hdr, - const char *fw_name, const char *fw_tag) -{ - struct device *dev = i1480->dev; - ssize_t result = 0; - struct fw_hdr *hdr_itr; - int verif_retry_count; - - /* Now, header by header, push them to the hw */ - for (hdr_itr = hdr; hdr_itr != NULL; hdr_itr = hdr_itr->next) { - verif_retry_count = 0; -retry: - dev_dbg(dev, "fw chunk (%zu @ 0x%08lx)\n", - hdr_itr->length * sizeof(hdr_itr->bin[0]), - hdr_itr->address); - result = i1480->write(i1480, hdr_itr->address, hdr_itr->bin, - hdr_itr->length*sizeof(hdr_itr->bin[0])); - if (result < 0) { - dev_err(dev, "%s fw '%s': write failed (%zuB @ 0x%lx):" - " %zd\n", fw_tag, fw_name, - hdr_itr->length * sizeof(hdr_itr->bin[0]), - hdr_itr->address, result); - break; - } - result = i1480_fw_cmp(i1480, hdr_itr); - if (result < 0) { - dev_err(dev, "%s fw '%s': verification read " - "failed (%zuB @ 0x%lx): %zd\n", - fw_tag, fw_name, - hdr_itr->length * sizeof(hdr_itr->bin[0]), - hdr_itr->address, result); - break; - } - if (result > 0) { /* Offset where it failed + 1 */ - result--; - dev_err(dev, "%s fw '%s': WARNING: verification " - "failed at 0x%lx: retrying\n", - fw_tag, fw_name, hdr_itr->address + result); - if (++verif_retry_count < 3) - goto retry; /* write this block again! */ - dev_err(dev, "%s fw '%s': verification failed at 0x%lx: " - "tried %d times\n", fw_tag, fw_name, - hdr_itr->address + result, verif_retry_count); - result = -EINVAL; - break; - } - } - return result; -} - - -/** Puts the device in firmware upload mode.*/ -static -int mac_fw_upload_enable(struct i1480 *i1480) -{ - int result; - u32 reg = 0x800000c0; - u32 *buffer = (u32 *)i1480->cmd_buf; - - if (i1480->hw_rev > 1) - reg = 0x8000d0d4; - result = i1480->read(i1480, reg, sizeof(u32)); - if (result < 0) - goto error_cmd; - *buffer &= ~i1480_FW_UPLOAD_MODE_MASK; - result = i1480->write(i1480, reg, buffer, sizeof(u32)); - if (result < 0) - goto error_cmd; - return 0; -error_cmd: - dev_err(i1480->dev, "can't enable fw upload mode: %d\n", result); - return result; -} - - -/** Gets the device out of firmware upload mode. */ -static -int mac_fw_upload_disable(struct i1480 *i1480) -{ - int result; - u32 reg = 0x800000c0; - u32 *buffer = (u32 *)i1480->cmd_buf; - - if (i1480->hw_rev > 1) - reg = 0x8000d0d4; - result = i1480->read(i1480, reg, sizeof(u32)); - if (result < 0) - goto error_cmd; - *buffer |= i1480_FW_UPLOAD_MODE_MASK; - result = i1480->write(i1480, reg, buffer, sizeof(u32)); - if (result < 0) - goto error_cmd; - return 0; -error_cmd: - dev_err(i1480->dev, "can't disable fw upload mode: %d\n", result); - return result; -} - - - -/** - * Generic function for uploading a MAC firmware. - * - * @i1480: Device instance - * @fw_name: Name of firmware file to upload. - * @fw_tag: Name of the firmware type (for messages) - * [eg: MAC, PRE] - * @do_wait: Wait for device to emit initialization done message (0 - * for PRE fws, 1 for MAC fws). - * @returns: 0 if ok, < 0 errno on error. - */ -static -int __mac_fw_upload(struct i1480 *i1480, const char *fw_name, - const char *fw_tag) -{ - int result; - const struct firmware *fw; - struct fw_hdr *fw_hdrs; - - result = request_firmware(&fw, fw_name, i1480->dev); - if (result < 0) /* Up to caller to complain on -ENOENT */ - goto out; - result = fw_hdrs_load(i1480, &fw_hdrs, fw->data, fw->size); - if (result < 0) { - dev_err(i1480->dev, "%s fw '%s': failed to parse firmware " - "file: %d\n", fw_tag, fw_name, result); - goto out_release; - } - result = mac_fw_upload_enable(i1480); - if (result < 0) - goto out_hdrs_release; - result = mac_fw_hdrs_push(i1480, fw_hdrs, fw_name, fw_tag); - mac_fw_upload_disable(i1480); -out_hdrs_release: - if (result >= 0) - dev_info(i1480->dev, "%s fw '%s': uploaded\n", fw_tag, fw_name); - else - dev_err(i1480->dev, "%s fw '%s': failed to upload (%d), " - "power cycle device\n", fw_tag, fw_name, result); - fw_hdrs_free(fw_hdrs); -out_release: - release_firmware(fw); -out: - return result; -} - - -/** - * Upload a pre-PHY firmware - * - */ -int i1480_pre_fw_upload(struct i1480 *i1480) -{ - int result; - result = __mac_fw_upload(i1480, i1480->pre_fw_name, "PRE"); - if (result == 0) - msleep(400); - return result; -} - - -/** - * Reset a the MAC and PHY - * - * @i1480: Device's instance - * @returns: 0 if ok, < 0 errno code on error - * - * We put the command on kmalloc'ed memory as some arches cannot do - * USB from the stack. The reply event is copied from an stage buffer, - * so it can be in the stack. See WUSB1.0[8.6.2.4] for more details. - * - * We issue the reset to make sure the UWB controller reinits the PHY; - * this way we can now if the PHY init went ok. - */ -static -int i1480_cmd_reset(struct i1480 *i1480) -{ - int result; - struct uwb_rccb *cmd = (void *) i1480->cmd_buf; - struct i1480_evt_reset { - struct uwb_rceb rceb; - u8 bResultCode; - } __attribute__((packed)) *reply = (void *) i1480->evt_buf; - - result = -ENOMEM; - cmd->bCommandType = UWB_RC_CET_GENERAL; - cmd->wCommand = cpu_to_le16(UWB_RC_CMD_RESET); - reply->rceb.bEventType = UWB_RC_CET_GENERAL; - reply->rceb.wEvent = UWB_RC_CMD_RESET; - result = i1480_cmd(i1480, "RESET", sizeof(*cmd), sizeof(*reply)); - if (result < 0) - goto out; - if (reply->bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(i1480->dev, "RESET: command execution failed: %u\n", - reply->bResultCode); - result = -EIO; - } -out: - return result; - -} - - -/* Wait for the MAC FW to start running */ -static -int i1480_fw_is_running_q(struct i1480 *i1480) -{ - int cnt = 0; - int result; - u32 *val = (u32 *) i1480->cmd_buf; - - for (cnt = 0; cnt < 10; cnt++) { - msleep(100); - result = i1480->read(i1480, 0x80080000, 4); - if (result < 0) { - dev_err(i1480->dev, "Can't read 0x8008000: %d\n", result); - goto out; - } - if (*val == 0x55555555UL) /* fw running? cool */ - goto out; - } - dev_err(i1480->dev, "Timed out waiting for fw to start\n"); - result = -ETIMEDOUT; -out: - return result; - -} - - -/** - * Upload MAC firmware, wait for it to start - * - * @i1480: Device instance - * @fw_name: Name of the file that contains the firmware - * - * This has to be called after the pre fw has been uploaded (if - * there is any). - */ -int i1480_mac_fw_upload(struct i1480 *i1480) -{ - int result = 0, deprecated_name = 0; - struct i1480_rceb *rcebe = (void *) i1480->evt_buf; - - result = __mac_fw_upload(i1480, i1480->mac_fw_name, "MAC"); - if (result == -ENOENT) { - result = __mac_fw_upload(i1480, i1480->mac_fw_name_deprecate, - "MAC"); - deprecated_name = 1; - } - if (result < 0) - return result; - if (deprecated_name == 1) - dev_warn(i1480->dev, - "WARNING: firmware file name %s is deprecated, " - "please rename to %s\n", - i1480->mac_fw_name_deprecate, i1480->mac_fw_name); - result = i1480_fw_is_running_q(i1480); - if (result < 0) - goto error_fw_not_running; - result = i1480->rc_setup ? i1480->rc_setup(i1480) : 0; - if (result < 0) { - dev_err(i1480->dev, "Cannot setup after MAC fw upload: %d\n", - result); - goto error_setup; - } - result = i1480->wait_init_done(i1480); /* wait init'on */ - if (result < 0) { - dev_err(i1480->dev, "MAC fw '%s': Initialization timed out " - "(%d)\n", i1480->mac_fw_name, result); - goto error_init_timeout; - } - /* verify we got the right initialization done event */ - if (i1480->evt_result != sizeof(*rcebe)) { - dev_err(i1480->dev, "MAC fw '%s': initialization event returns " - "wrong size (%zu bytes vs %zu needed)\n", - i1480->mac_fw_name, i1480->evt_result, sizeof(*rcebe)); - goto error_size; - } - result = -EIO; - if (i1480_rceb_check(i1480, &rcebe->rceb, NULL, 0, i1480_CET_VS1, - i1480_EVT_RM_INIT_DONE) < 0) { - dev_err(i1480->dev, "wrong initialization event 0x%02x/%04x/%02x " - "received; expected 0x%02x/%04x/00\n", - rcebe->rceb.bEventType, le16_to_cpu(rcebe->rceb.wEvent), - rcebe->rceb.bEventContext, i1480_CET_VS1, - i1480_EVT_RM_INIT_DONE); - goto error_init_timeout; - } - result = i1480_cmd_reset(i1480); - if (result < 0) - dev_err(i1480->dev, "MAC fw '%s': MBOA reset failed (%d)\n", - i1480->mac_fw_name, result); -error_fw_not_running: -error_init_timeout: -error_size: -error_setup: - return result; -} diff --git a/drivers/staging/uwb/i1480/dfu/phy.c b/drivers/staging/uwb/i1480/dfu/phy.c deleted file mode 100644 index 13512c7dda0b..000000000000 --- a/drivers/staging/uwb/i1480/dfu/phy.c +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Wireless UWB Link 1480 - * PHY parameters upload - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * Code for uploading the PHY parameters to the PHY through the UWB - * Radio Control interface. - * - * We just send the data through the MPI interface using HWA-like - * commands and then reset the PHY to make sure it is ok. - */ -#include -#include -#include -#include "../../../wusbcore/include/wusb.h" -#include "i1480-dfu.h" - - -/** - * Write a value array to an address of the MPI interface - * - * @i1480: Device descriptor - * @data: Data array to write - * @size: Size of the data array - * @returns: 0 if ok, < 0 errno code on error. - * - * The data array is organized into pairs: - * - * ADDRESS VALUE - * - * ADDRESS is BE 16 bit unsigned, VALUE 8 bit unsigned. Size thus has - * to be a multiple of three. - */ -static -int i1480_mpi_write(struct i1480 *i1480, const void *data, size_t size) -{ - int result; - struct i1480_cmd_mpi_write *cmd = i1480->cmd_buf; - struct i1480_evt_confirm *reply = i1480->evt_buf; - - BUG_ON(size > 480); - result = -ENOMEM; - cmd->rccb.bCommandType = i1480_CET_VS1; - cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_WRITE); - cmd->size = cpu_to_le16(size); - memcpy(cmd->data, data, size); - reply->rceb.bEventType = i1480_CET_VS1; - reply->rceb.wEvent = i1480_CMD_MPI_WRITE; - result = i1480_cmd(i1480, "MPI-WRITE", sizeof(*cmd) + size, sizeof(*reply)); - if (result < 0) - goto out; - if (reply->bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(i1480->dev, "MPI-WRITE: command execution failed: %d\n", - reply->bResultCode); - result = -EIO; - } -out: - return result; -} - - -/** - * Read a value array to from an address of the MPI interface - * - * @i1480: Device descriptor - * @data: where to place the read array - * @srcaddr: Where to read from - * @size: Size of the data read array - * @returns: 0 if ok, < 0 errno code on error. - * - * The command data array is organized into pairs ADDR0 ADDR1..., and - * the returned data in ADDR0 VALUE0 ADDR1 VALUE1... - * - * We generate the command array to be a sequential read and then - * rearrange the result. - * - * We use the i1480->cmd_buf for the command, i1480->evt_buf for the reply. - * - * As the reply has to fit in 512 bytes (i1480->evt_buffer), the max amount - * of values we can read is (512 - sizeof(*reply)) / 3 - */ -static -int i1480_mpi_read(struct i1480 *i1480, u8 *data, u16 srcaddr, size_t size) -{ - int result; - struct i1480_cmd_mpi_read *cmd = i1480->cmd_buf; - struct i1480_evt_mpi_read *reply = i1480->evt_buf; - unsigned cnt; - - memset(i1480->cmd_buf, 0x69, 512); - memset(i1480->evt_buf, 0x69, 512); - - BUG_ON(size > (i1480->buf_size - sizeof(*reply)) / 3); - result = -ENOMEM; - cmd->rccb.bCommandType = i1480_CET_VS1; - cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_READ); - cmd->size = cpu_to_le16(3*size); - for (cnt = 0; cnt < size; cnt++) { - cmd->data[cnt].page = (srcaddr + cnt) >> 8; - cmd->data[cnt].offset = (srcaddr + cnt) & 0xff; - } - reply->rceb.bEventType = i1480_CET_VS1; - reply->rceb.wEvent = i1480_CMD_MPI_READ; - result = i1480_cmd(i1480, "MPI-READ", sizeof(*cmd) + 2*size, - sizeof(*reply) + 3*size); - if (result < 0) - goto out; - if (reply->bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(i1480->dev, "MPI-READ: command execution failed: %d\n", - reply->bResultCode); - result = -EIO; - goto out; - } - for (cnt = 0; cnt < size; cnt++) { - if (reply->data[cnt].page != (srcaddr + cnt) >> 8) - dev_err(i1480->dev, "MPI-READ: page inconsistency at " - "index %u: expected 0x%02x, got 0x%02x\n", cnt, - (srcaddr + cnt) >> 8, reply->data[cnt].page); - if (reply->data[cnt].offset != ((srcaddr + cnt) & 0x00ff)) - dev_err(i1480->dev, "MPI-READ: offset inconsistency at " - "index %u: expected 0x%02x, got 0x%02x\n", cnt, - (srcaddr + cnt) & 0x00ff, - reply->data[cnt].offset); - data[cnt] = reply->data[cnt].value; - } - result = 0; -out: - return result; -} - - -/** - * Upload a PHY firmware, wait for it to start - * - * @i1480: Device instance - * @fw_name: Name of the file that contains the firmware - * - * We assume the MAC fw is up and running. This means we can use the - * MPI interface to write the PHY firmware. Once done, we issue an - * MBOA Reset, which will force the MAC to reset and reinitialize the - * PHY. If that works, we are ready to go. - * - * Max packet size for the MPI write is 512, so the max buffer is 480 - * (which gives us 160 byte triads of MSB, LSB and VAL for the data). - */ -int i1480_phy_fw_upload(struct i1480 *i1480) -{ - int result; - const struct firmware *fw; - const char *data_itr, *data_top; - const size_t MAX_BLK_SIZE = 480; /* 160 triads */ - size_t data_size; - u8 phy_stat; - - result = request_firmware(&fw, i1480->phy_fw_name, i1480->dev); - if (result < 0) - goto out; - /* Loop writing data in chunks as big as possible until done. */ - for (data_itr = fw->data, data_top = data_itr + fw->size; - data_itr < data_top; data_itr += MAX_BLK_SIZE) { - data_size = min(MAX_BLK_SIZE, (size_t) (data_top - data_itr)); - result = i1480_mpi_write(i1480, data_itr, data_size); - if (result < 0) - goto error_mpi_write; - } - /* Read MPI page 0, offset 6; if 0, PHY was initialized correctly. */ - result = i1480_mpi_read(i1480, &phy_stat, 0x0006, 1); - if (result < 0) { - dev_err(i1480->dev, "PHY: can't get status: %d\n", result); - goto error_mpi_status; - } - if (phy_stat != 0) { - result = -ENODEV; - dev_info(i1480->dev, "error, PHY not ready: %u\n", phy_stat); - goto error_phy_status; - } - dev_info(i1480->dev, "PHY fw '%s': uploaded\n", i1480->phy_fw_name); -error_phy_status: -error_mpi_status: -error_mpi_write: - release_firmware(fw); - if (result < 0) - dev_err(i1480->dev, "PHY fw '%s': failed to upload (%d), " - "power cycle device\n", i1480->phy_fw_name, result); -out: - return result; -} diff --git a/drivers/staging/uwb/i1480/dfu/usb.c b/drivers/staging/uwb/i1480/dfu/usb.c deleted file mode 100644 index d41086bdd783..000000000000 --- a/drivers/staging/uwb/i1480/dfu/usb.c +++ /dev/null @@ -1,448 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Wireless UWB Link 1480 - * USB SKU firmware upload implementation - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This driver will prepare the i1480 device to behave as a real - * Wireless USB HWA adaptor by uploading the firmware. - * - * When the device is connected or driver is loaded, i1480_usb_probe() - * is called--this will allocate and initialize the device structure, - * fill in the pointers to the common functions (read, write, - * wait_init_done and cmd for HWA command execution) and once that is - * done, call the common firmware uploading routine. Then clean up and - * return -ENODEV, as we don't attach to the device. - * - * The rest are the basic ops we implement that the fw upload code - * uses to do its job. All the ops in the common code are i1480->NAME, - * the functions are i1480_usb_NAME(). - */ -#include -#include -#include -#include -#include -#include "../../uwb.h" -#include "../../../wusbcore/include/wusb.h" -#include "../../../wusbcore/include/wusb-wa.h" -#include "i1480-dfu.h" - -struct i1480_usb { - struct i1480 i1480; - struct usb_device *usb_dev; - struct usb_interface *usb_iface; - struct urb *neep_urb; /* URB for reading from EP1 */ -}; - - -static -void i1480_usb_init(struct i1480_usb *i1480_usb) -{ - i1480_init(&i1480_usb->i1480); -} - - -static -int i1480_usb_create(struct i1480_usb *i1480_usb, struct usb_interface *iface) -{ - struct usb_device *usb_dev = interface_to_usbdev(iface); - int result = -ENOMEM; - - i1480_usb->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */ - i1480_usb->usb_iface = usb_get_intf(iface); - usb_set_intfdata(iface, i1480_usb); /* Bind the driver to iface0 */ - i1480_usb->neep_urb = usb_alloc_urb(0, GFP_KERNEL); - if (i1480_usb->neep_urb == NULL) - goto error; - return 0; - -error: - usb_set_intfdata(iface, NULL); - usb_put_intf(iface); - usb_put_dev(usb_dev); - return result; -} - - -static -void i1480_usb_destroy(struct i1480_usb *i1480_usb) -{ - usb_kill_urb(i1480_usb->neep_urb); - usb_free_urb(i1480_usb->neep_urb); - usb_set_intfdata(i1480_usb->usb_iface, NULL); - usb_put_intf(i1480_usb->usb_iface); - usb_put_dev(i1480_usb->usb_dev); -} - - -/** - * Write a buffer to a memory address in the i1480 device - * - * @i1480: i1480 instance - * @memory_address: - * Address where to write the data buffer to. - * @buffer: Buffer to the data - * @size: Size of the buffer [has to be < 512]. - * @returns: 0 if ok, < 0 errno code on error. - * - * Data buffers to USB cannot be on the stack or in vmalloc'ed areas, - * so we copy it to the local i1480 buffer before proceeding. In any - * case, we have a max size we can send. - */ -static -int i1480_usb_write(struct i1480 *i1480, u32 memory_address, - const void *buffer, size_t size) -{ - int result = 0; - struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480); - size_t buffer_size, itr = 0; - - BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */ - while (size > 0) { - buffer_size = size < i1480->buf_size ? size : i1480->buf_size; - memcpy(i1480->cmd_buf, buffer + itr, buffer_size); - result = usb_control_msg( - i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0), - 0xf0, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - memory_address, (memory_address >> 16), - i1480->cmd_buf, buffer_size, 100 /* FIXME: arbitrary */); - if (result < 0) - break; - itr += result; - memory_address += result; - size -= result; - } - return result; -} - - -/** - * Read a block [max size 512] of the device's memory to @i1480's buffer. - * - * @i1480: i1480 instance - * @memory_address: - * Address where to read from. - * @size: Size to read. Smaller than or equal to 512. - * @returns: >= 0 number of bytes written if ok, < 0 errno code on error. - * - * NOTE: if the memory address or block is incorrect, you might get a - * stall or a different memory read. Caller has to verify the - * memory address and size passed back in the @neh structure. - */ -static -int i1480_usb_read(struct i1480 *i1480, u32 addr, size_t size) -{ - ssize_t result = 0, bytes = 0; - size_t itr, read_size = i1480->buf_size; - struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480); - - BUG_ON(size > i1480->buf_size); - BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */ - BUG_ON(read_size > 512); - - if (addr >= 0x8000d200 && addr < 0x8000d400) /* Yeah, HW quirk */ - read_size = 4; - - for (itr = 0; itr < size; itr += read_size) { - size_t itr_addr = addr + itr; - size_t itr_size = min(read_size, size - itr); - result = usb_control_msg( - i1480_usb->usb_dev, usb_rcvctrlpipe(i1480_usb->usb_dev, 0), - 0xf0, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - itr_addr, (itr_addr >> 16), - i1480->cmd_buf + itr, itr_size, - 100 /* FIXME: arbitrary */); - if (result < 0) { - dev_err(i1480->dev, "%s: USB read error: %zd\n", - __func__, result); - goto out; - } - if (result != itr_size) { - result = -EIO; - dev_err(i1480->dev, - "%s: partial read got only %zu bytes vs %zu expected\n", - __func__, result, itr_size); - goto out; - } - bytes += result; - } - result = bytes; -out: - return result; -} - - -/** - * Callback for reads on the notification/event endpoint - * - * Just enables the completion read handler. - */ -static -void i1480_usb_neep_cb(struct urb *urb) -{ - struct i1480 *i1480 = urb->context; - struct device *dev = i1480->dev; - - switch (urb->status) { - case 0: - break; - case -ECONNRESET: /* Not an error, but a controlled situation; */ - case -ENOENT: /* (we killed the URB)...so, no broadcast */ - dev_dbg(dev, "NEEP: reset/noent %d\n", urb->status); - break; - case -ESHUTDOWN: /* going away! */ - dev_dbg(dev, "NEEP: down %d\n", urb->status); - break; - default: - dev_err(dev, "NEEP: unknown status %d\n", urb->status); - break; - } - i1480->evt_result = urb->actual_length; - complete(&i1480->evt_complete); - return; -} - - -/** - * Wait for the MAC FW to initialize - * - * MAC FW sends a 0xfd/0101/00 notification to EP1 when done - * initializing. Get that notification into i1480->evt_buf; upper layer - * will verify it. - * - * Set i1480->evt_result with the result of getting the event or its - * size (if successful). - * - * Delivers the data directly to i1480->evt_buf - */ -static -int i1480_usb_wait_init_done(struct i1480 *i1480) -{ - int result; - struct device *dev = i1480->dev; - struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480); - struct usb_endpoint_descriptor *epd; - - init_completion(&i1480->evt_complete); - i1480->evt_result = -EINPROGRESS; - epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc; - usb_fill_int_urb(i1480_usb->neep_urb, i1480_usb->usb_dev, - usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress), - i1480->evt_buf, i1480->buf_size, - i1480_usb_neep_cb, i1480, epd->bInterval); - result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL); - if (result < 0) { - dev_err(dev, "init done: cannot submit NEEP read: %d\n", - result); - goto error_submit; - } - /* Wait for the USB callback to get the data */ - result = wait_for_completion_interruptible_timeout( - &i1480->evt_complete, HZ); - if (result <= 0) { - result = result == 0 ? -ETIMEDOUT : result; - goto error_wait; - } - usb_kill_urb(i1480_usb->neep_urb); - return 0; - -error_wait: - usb_kill_urb(i1480_usb->neep_urb); -error_submit: - i1480->evt_result = result; - return result; -} - - -/** - * Generic function for issuing commands to the i1480 - * - * @i1480: i1480 instance - * @cmd_name: Name of the command (for error messages) - * @cmd: Pointer to command buffer - * @cmd_size: Size of the command buffer - * @reply: Buffer for the reply event - * @reply_size: Expected size back (including RCEB); the reply buffer - * is assumed to be as big as this. - * @returns: >= 0 size of the returned event data if ok, - * < 0 errno code on error. - * - * Arms the NE handle, issues the command to the device and checks the - * basics of the reply event. - */ -static -int i1480_usb_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size) -{ - int result; - struct device *dev = i1480->dev; - struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480); - struct usb_endpoint_descriptor *epd; - struct uwb_rccb *cmd = i1480->cmd_buf; - u8 iface_no; - - /* Post a read on the notification & event endpoint */ - iface_no = i1480_usb->usb_iface->cur_altsetting->desc.bInterfaceNumber; - epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc; - usb_fill_int_urb( - i1480_usb->neep_urb, i1480_usb->usb_dev, - usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress), - i1480->evt_buf, i1480->buf_size, - i1480_usb_neep_cb, i1480, epd->bInterval); - result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL); - if (result < 0) { - dev_err(dev, "%s: cannot submit NEEP read: %d\n", - cmd_name, result); - goto error_submit_ep1; - } - /* Now post the command on EP0 */ - result = usb_control_msg( - i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0), - WA_EXEC_RC_CMD, - USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS, - 0, iface_no, - cmd, cmd_size, - 100 /* FIXME: this is totally arbitrary */); - if (result < 0) { - dev_err(dev, "%s: control request failed: %d\n", - cmd_name, result); - goto error_submit_ep0; - } - return result; - -error_submit_ep0: - usb_kill_urb(i1480_usb->neep_urb); -error_submit_ep1: - return result; -} - - -/* - * Probe a i1480 device for uploading firmware. - * - * We attach only to interface #0, which is the radio control interface. - */ -static -int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(iface); - struct i1480_usb *i1480_usb; - struct i1480 *i1480; - struct device *dev = &iface->dev; - int result; - - result = -ENODEV; - if (iface->cur_altsetting->desc.bInterfaceNumber != 0) { - dev_dbg(dev, "not attaching to iface %d\n", - iface->cur_altsetting->desc.bInterfaceNumber); - goto error; - } - if (iface->num_altsetting > 1 && - le16_to_cpu(udev->descriptor.idProduct) == 0xbabe) { - /* Need altsetting #1 [HW QUIRK] or EP1 won't work */ - result = usb_set_interface(interface_to_usbdev(iface), 0, 1); - if (result < 0) - dev_warn(dev, - "can't set altsetting 1 on iface 0: %d\n", - result); - } - - if (iface->cur_altsetting->desc.bNumEndpoints < 1) - return -ENODEV; - - result = -ENOMEM; - i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL); - if (i1480_usb == NULL) { - dev_err(dev, "Unable to allocate instance\n"); - goto error; - } - i1480_usb_init(i1480_usb); - - i1480 = &i1480_usb->i1480; - i1480->buf_size = 512; - i1480->cmd_buf = kmalloc_array(2, i1480->buf_size, GFP_KERNEL); - if (i1480->cmd_buf == NULL) { - dev_err(dev, "Cannot allocate transfer buffers\n"); - result = -ENOMEM; - goto error_buf_alloc; - } - i1480->evt_buf = i1480->cmd_buf + i1480->buf_size; - - result = i1480_usb_create(i1480_usb, iface); - if (result < 0) { - dev_err(dev, "Cannot create instance: %d\n", result); - goto error_create; - } - - /* setup the fops and upload the firmware */ - i1480->pre_fw_name = "i1480-pre-phy-0.0.bin"; - i1480->mac_fw_name = "i1480-usb-0.0.bin"; - i1480->mac_fw_name_deprecate = "ptc-0.0.bin"; - i1480->phy_fw_name = "i1480-phy-0.0.bin"; - i1480->dev = &iface->dev; - i1480->write = i1480_usb_write; - i1480->read = i1480_usb_read; - i1480->rc_setup = NULL; - i1480->wait_init_done = i1480_usb_wait_init_done; - i1480->cmd = i1480_usb_cmd; - - result = i1480_fw_upload(&i1480_usb->i1480); /* the real thing */ - if (result >= 0) { - usb_reset_device(i1480_usb->usb_dev); - result = -ENODEV; /* we don't want to bind to the iface */ - } - i1480_usb_destroy(i1480_usb); -error_create: - kfree(i1480->cmd_buf); -error_buf_alloc: - kfree(i1480_usb); -error: - return result; -} - -MODULE_FIRMWARE("i1480-pre-phy-0.0.bin"); -MODULE_FIRMWARE("i1480-usb-0.0.bin"); -MODULE_FIRMWARE("i1480-phy-0.0.bin"); - -#define i1480_USB_DEV(v, p) \ -{ \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE \ - | USB_DEVICE_ID_MATCH_DEV_INFO \ - | USB_DEVICE_ID_MATCH_INT_INFO, \ - .idVendor = (v), \ - .idProduct = (p), \ - .bDeviceClass = 0xff, \ - .bDeviceSubClass = 0xff, \ - .bDeviceProtocol = 0xff, \ - .bInterfaceClass = 0xff, \ - .bInterfaceSubClass = 0xff, \ - .bInterfaceProtocol = 0xff, \ -} - - -/** USB device ID's that we handle */ -static const struct usb_device_id i1480_usb_id_table[] = { - i1480_USB_DEV(0x8086, 0xdf3b), - i1480_USB_DEV(0x15a9, 0x0005), - i1480_USB_DEV(0x07d1, 0x3802), - i1480_USB_DEV(0x050d, 0x305a), - i1480_USB_DEV(0x3495, 0x3007), - {}, -}; -MODULE_DEVICE_TABLE(usb, i1480_usb_id_table); - - -static struct usb_driver i1480_dfu_driver = { - .name = "i1480-dfu-usb", - .id_table = i1480_usb_id_table, - .probe = i1480_usb_probe, - .disconnect = NULL, -}; - -module_usb_driver(i1480_dfu_driver); - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("Intel Wireless UWB Link 1480 firmware uploader for USB"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/uwb/i1480/i1480-est.c b/drivers/staging/uwb/i1480/i1480-est.c deleted file mode 100644 index 106e0a44b138..000000000000 --- a/drivers/staging/uwb/i1480/i1480-est.c +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Wireless UWB Link 1480 - * Event Size tables for Wired Adaptors - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - */ - -#include -#include -#include -#include "../uwb.h" -#include "dfu/i1480-dfu.h" - - -/** Event size table for wEvents 0x00XX */ -static struct uwb_est_entry i1480_est_fd00[] = { - /* Anybody expecting this response has to use - * neh->extra_size to specify the real size that will - * come back. */ - [i1480_EVT_CONFIRM] = { .size = sizeof(struct i1480_evt_confirm) }, - [i1480_CMD_SET_IP_MAS] = { .size = sizeof(struct i1480_evt_confirm) }, -#ifdef i1480_RCEB_EXTENDED - [0x09] = { - .size = sizeof(struct i1480_rceb), - .offset = 1 + offsetof(struct i1480_rceb, wParamLength), - }, -#endif -}; - -/** Event size table for wEvents 0x01XX */ -static struct uwb_est_entry i1480_est_fd01[] = { - [0xff & i1480_EVT_RM_INIT_DONE] = { .size = sizeof(struct i1480_rceb) }, - [0xff & i1480_EVT_DEV_ADD] = { .size = sizeof(struct i1480_rceb) + 9 }, - [0xff & i1480_EVT_DEV_RM] = { .size = sizeof(struct i1480_rceb) + 9 }, - [0xff & i1480_EVT_DEV_ID_CHANGE] = { - .size = sizeof(struct i1480_rceb) + 2 }, -}; - -static int __init i1480_est_init(void) -{ - int result = uwb_est_register(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b, - i1480_est_fd00, - ARRAY_SIZE(i1480_est_fd00)); - if (result < 0) { - printk(KERN_ERR "Can't register EST table fd00: %d\n", result); - return result; - } - result = uwb_est_register(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b, - i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01)); - if (result < 0) { - printk(KERN_ERR "Can't register EST table fd01: %d\n", result); - return result; - } - return 0; -} -module_init(i1480_est_init); - -static void __exit i1480_est_exit(void) -{ - uwb_est_unregister(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b, - i1480_est_fd00, ARRAY_SIZE(i1480_est_fd00)); - uwb_est_unregister(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b, - i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01)); -} -module_exit(i1480_est_exit); - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("i1480's Vendor Specific Event Size Tables"); -MODULE_LICENSE("GPL"); - -/** - * USB device ID's that we handle - * - * [so we are loaded when this kind device is connected] - */ -static struct usb_device_id __used i1480_est_id_table[] = { - { USB_DEVICE(0x8086, 0xdf3b), }, - { USB_DEVICE(0x8086, 0x0c3b), }, - { }, -}; -MODULE_DEVICE_TABLE(usb, i1480_est_id_table); diff --git a/drivers/staging/uwb/ie-rcv.c b/drivers/staging/uwb/ie-rcv.c deleted file mode 100644 index 51a4706e0dd3..000000000000 --- a/drivers/staging/uwb/ie-rcv.c +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * IE Received notification handling. - * - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ - -#include -#include -#include -#include -#include "uwb-internal.h" - -/* - * Process an incoming IE Received notification. - */ -int uwbd_evt_handle_rc_ie_rcv(struct uwb_event *evt) -{ - int result = -EINVAL; - struct device *dev = &evt->rc->uwb_dev.dev; - struct uwb_rc_evt_ie_rcv *iercv; - - /* Is there enough data to decode it? */ - if (evt->notif.size < sizeof(*iercv)) { - dev_err(dev, "IE Received notification: Not enough data to " - "decode (%zu vs %zu bytes needed)\n", - evt->notif.size, sizeof(*iercv)); - goto error; - } - iercv = container_of(evt->notif.rceb, struct uwb_rc_evt_ie_rcv, rceb); - - dev_dbg(dev, "IE received, element ID=%d\n", iercv->IEData[0]); - - if (iercv->IEData[0] == UWB_RELINQUISH_REQUEST_IE) { - dev_warn(dev, "unhandled Relinquish Request IE\n"); - } - - return 0; -error: - return result; -} diff --git a/drivers/staging/uwb/ie.c b/drivers/staging/uwb/ie.c deleted file mode 100644 index b11678dd0380..000000000000 --- a/drivers/staging/uwb/ie.c +++ /dev/null @@ -1,366 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Information Element Handling - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * Reinette Chatre - * - * FIXME: docs - */ - -#include -#include -#include "uwb-internal.h" - -/** - * uwb_ie_next - get the next IE in a buffer - * @ptr: start of the buffer containing the IE data - * @len: length of the buffer - * - * Both @ptr and @len are updated so subsequent calls to uwb_ie_next() - * will get the next IE. - * - * NULL is returned (and @ptr and @len will not be updated) if there - * are no more IEs in the buffer or the buffer is too short. - */ -struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len) -{ - struct uwb_ie_hdr *hdr; - size_t ie_len; - - if (*len < sizeof(struct uwb_ie_hdr)) - return NULL; - - hdr = *ptr; - ie_len = sizeof(struct uwb_ie_hdr) + hdr->length; - - if (*len < ie_len) - return NULL; - - *ptr += ie_len; - *len -= ie_len; - - return hdr; -} -EXPORT_SYMBOL_GPL(uwb_ie_next); - -/** - * uwb_ie_dump_hex - print IEs to a character buffer - * @ies: the IEs to print. - * @len: length of all the IEs. - * @buf: the destination buffer. - * @size: size of @buf. - * - * Returns the number of characters written. - */ -int uwb_ie_dump_hex(const struct uwb_ie_hdr *ies, size_t len, - char *buf, size_t size) -{ - void *ptr; - const struct uwb_ie_hdr *ie; - int r = 0; - u8 *d; - - ptr = (void *)ies; - for (;;) { - ie = uwb_ie_next(&ptr, &len); - if (!ie) - break; - - r += scnprintf(buf + r, size - r, "%02x %02x", - (unsigned)ie->element_id, - (unsigned)ie->length); - d = (uint8_t *)ie + sizeof(struct uwb_ie_hdr); - while (d != ptr && r < size) - r += scnprintf(buf + r, size - r, " %02x", (unsigned)*d++); - if (r < size) - buf[r++] = '\n'; - }; - - return r; -} - -/** - * Get the IEs that a radio controller is sending in its beacon - * - * @uwb_rc: UWB Radio Controller - * @returns: Size read from the system - * - * We don't need to lock the uwb_rc's mutex because we don't modify - * anything. Once done with the iedata buffer, call - * uwb_rc_ie_release(iedata). Don't call kfree on it. - */ -static -ssize_t uwb_rc_get_ie(struct uwb_rc *uwb_rc, struct uwb_rc_evt_get_ie **pget_ie) -{ - ssize_t result; - struct device *dev = &uwb_rc->uwb_dev.dev; - struct uwb_rccb *cmd = NULL; - struct uwb_rceb *reply = NULL; - struct uwb_rc_evt_get_ie *get_ie; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->bCommandType = UWB_RC_CET_GENERAL; - cmd->wCommand = cpu_to_le16(UWB_RC_CMD_GET_IE); - result = uwb_rc_vcmd(uwb_rc, "GET_IE", cmd, sizeof(*cmd), - UWB_RC_CET_GENERAL, UWB_RC_CMD_GET_IE, - &reply); - kfree(cmd); - if (result < 0) - return result; - - get_ie = container_of(reply, struct uwb_rc_evt_get_ie, rceb); - if (result < sizeof(*get_ie)) { - dev_err(dev, "not enough data returned for decoding GET IE " - "(%zu bytes received vs %zu needed)\n", - result, sizeof(*get_ie)); - return -EINVAL; - } else if (result < sizeof(*get_ie) + le16_to_cpu(get_ie->wIELength)) { - dev_err(dev, "not enough data returned for decoding GET IE " - "payload (%zu bytes received vs %zu needed)\n", result, - sizeof(*get_ie) + le16_to_cpu(get_ie->wIELength)); - return -EINVAL; - } - - *pget_ie = get_ie; - return result; -} - - -/** - * Replace all IEs currently being transmitted by a device - * - * @cmd: pointer to the SET-IE command with the IEs to set - * @size: size of @buf - */ -int uwb_rc_set_ie(struct uwb_rc *rc, struct uwb_rc_cmd_set_ie *cmd) -{ - int result; - struct device *dev = &rc->uwb_dev.dev; - struct uwb_rc_evt_set_ie reply; - - reply.rceb.bEventType = UWB_RC_CET_GENERAL; - reply.rceb.wEvent = UWB_RC_CMD_SET_IE; - result = uwb_rc_cmd(rc, "SET-IE", &cmd->rccb, - sizeof(*cmd) + le16_to_cpu(cmd->wIELength), - &reply.rceb, sizeof(reply)); - if (result < 0) - goto error_cmd; - else if (result != sizeof(reply)) { - dev_err(dev, "SET-IE: not enough data to decode reply " - "(%d bytes received vs %zu needed)\n", - result, sizeof(reply)); - result = -EIO; - } else if (reply.bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(dev, "SET-IE: command execution failed: %s (%d)\n", - uwb_rc_strerror(reply.bResultCode), reply.bResultCode); - result = -EIO; - } else - result = 0; -error_cmd: - return result; -} - -/* Cleanup the whole IE management subsystem */ -void uwb_rc_ie_init(struct uwb_rc *uwb_rc) -{ - mutex_init(&uwb_rc->ies_mutex); -} - - -/** - * uwb_rc_ie_setup - setup a radio controller's IE manager - * @uwb_rc: the radio controller. - * - * The current set of IEs are obtained from the hardware with a GET-IE - * command (since the radio controller is not yet beaconing this will - * be just the hardware's MAC and PHY Capability IEs). - * - * Returns 0 on success; -ve on an error. - */ -int uwb_rc_ie_setup(struct uwb_rc *uwb_rc) -{ - struct uwb_rc_evt_get_ie *ie_info = NULL; - int capacity; - - capacity = uwb_rc_get_ie(uwb_rc, &ie_info); - if (capacity < 0) - return capacity; - - mutex_lock(&uwb_rc->ies_mutex); - - uwb_rc->ies = (struct uwb_rc_cmd_set_ie *)ie_info; - uwb_rc->ies->rccb.bCommandType = UWB_RC_CET_GENERAL; - uwb_rc->ies->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_IE); - uwb_rc->ies_capacity = capacity; - - mutex_unlock(&uwb_rc->ies_mutex); - - return 0; -} - - -/* Cleanup the whole IE management subsystem */ -void uwb_rc_ie_release(struct uwb_rc *uwb_rc) -{ - kfree(uwb_rc->ies); - uwb_rc->ies = NULL; - uwb_rc->ies_capacity = 0; -} - - -static int uwb_rc_ie_add_one(struct uwb_rc *rc, const struct uwb_ie_hdr *new_ie) -{ - struct uwb_rc_cmd_set_ie *new_ies; - void *ptr, *prev_ie; - struct uwb_ie_hdr *ie; - size_t length, new_ie_len, new_capacity, size, prev_size; - - length = le16_to_cpu(rc->ies->wIELength); - new_ie_len = sizeof(struct uwb_ie_hdr) + new_ie->length; - new_capacity = sizeof(struct uwb_rc_cmd_set_ie) + length + new_ie_len; - - if (new_capacity > rc->ies_capacity) { - new_ies = krealloc(rc->ies, new_capacity, GFP_KERNEL); - if (!new_ies) - return -ENOMEM; - rc->ies = new_ies; - } - - ptr = rc->ies->IEData; - size = length; - for (;;) { - prev_ie = ptr; - prev_size = size; - ie = uwb_ie_next(&ptr, &size); - if (!ie || ie->element_id > new_ie->element_id) - break; - } - - memmove(prev_ie + new_ie_len, prev_ie, prev_size); - memcpy(prev_ie, new_ie, new_ie_len); - rc->ies->wIELength = cpu_to_le16(length + new_ie_len); - - return 0; -} - -/** - * uwb_rc_ie_add - add new IEs to the radio controller's beacon - * @uwb_rc: the radio controller. - * @ies: the buffer containing the new IE or IEs to be added to - * the device's beacon. - * @size: length of all the IEs. - * - * According to WHCI 0.95 [4.13.6] the driver will only receive the RCEB - * after the device sent the first beacon that includes the IEs specified - * in the SET IE command. We thus cannot send this command if the device is - * not beaconing. Instead, a SET IE command will be sent later right after - * we start beaconing. - * - * Setting an IE on the device will overwrite all current IEs in device. So - * we take the current IEs being transmitted by the device, insert the - * new one, and call SET IE with all the IEs needed. - * - * Returns 0 on success; or -ENOMEM. - */ -int uwb_rc_ie_add(struct uwb_rc *uwb_rc, - const struct uwb_ie_hdr *ies, size_t size) -{ - int result = 0; - void *ptr; - const struct uwb_ie_hdr *ie; - - mutex_lock(&uwb_rc->ies_mutex); - - ptr = (void *)ies; - for (;;) { - ie = uwb_ie_next(&ptr, &size); - if (!ie) - break; - - result = uwb_rc_ie_add_one(uwb_rc, ie); - if (result < 0) - break; - } - if (result >= 0) { - if (size == 0) { - if (uwb_rc->beaconing != -1) - result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies); - } else - result = -EINVAL; - } - - mutex_unlock(&uwb_rc->ies_mutex); - - return result; -} -EXPORT_SYMBOL_GPL(uwb_rc_ie_add); - - -/* - * Remove an IE from internal cache - * - * We are dealing with our internal IE cache so no need to verify that the - * IEs are valid (it has been done already). - * - * Should be called with ies_mutex held - * - * We do not break out once an IE is found in the cache. It is currently - * possible to have more than one IE with the same ID included in the - * beacon. We don't reallocate, we just mark the size smaller. - */ -static -void uwb_rc_ie_cache_rm(struct uwb_rc *uwb_rc, enum uwb_ie to_remove) -{ - struct uwb_ie_hdr *ie; - size_t len = le16_to_cpu(uwb_rc->ies->wIELength); - void *ptr; - size_t size; - - ptr = uwb_rc->ies->IEData; - size = len; - for (;;) { - ie = uwb_ie_next(&ptr, &size); - if (!ie) - break; - if (ie->element_id == to_remove) { - len -= sizeof(struct uwb_ie_hdr) + ie->length; - memmove(ie, ptr, size); - ptr = ie; - } - } - uwb_rc->ies->wIELength = cpu_to_le16(len); -} - - -/** - * uwb_rc_ie_rm - remove an IE from the radio controller's beacon - * @uwb_rc: the radio controller. - * @element_id: the element ID of the IE to remove. - * - * Only IEs previously added with uwb_rc_ie_add() may be removed. - * - * Returns 0 on success; or -ve the SET-IE command to the radio - * controller failed. - */ -int uwb_rc_ie_rm(struct uwb_rc *uwb_rc, enum uwb_ie element_id) -{ - int result = 0; - - mutex_lock(&uwb_rc->ies_mutex); - - uwb_rc_ie_cache_rm(uwb_rc, element_id); - - if (uwb_rc->beaconing != -1) - result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies); - - mutex_unlock(&uwb_rc->ies_mutex); - - return result; -} -EXPORT_SYMBOL_GPL(uwb_rc_ie_rm); diff --git a/drivers/staging/uwb/include/debug-cmd.h b/drivers/staging/uwb/include/debug-cmd.h deleted file mode 100644 index f97db6c3bcc0..000000000000 --- a/drivers/staging/uwb/include/debug-cmd.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Ultra Wide Band - * Debug interface commands - * - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#ifndef __LINUX__UWB__DEBUG_CMD_H__ -#define __LINUX__UWB__DEBUG_CMD_H__ - -#include - -/* - * Debug interface commands - * - * UWB_DBG_CMD_RSV_ESTABLISH: Establish a new unicast reservation. - * - * UWB_DBG_CMD_RSV_TERMINATE: Terminate the Nth reservation. - */ - -enum uwb_dbg_cmd_type { - UWB_DBG_CMD_RSV_ESTABLISH = 1, - UWB_DBG_CMD_RSV_TERMINATE = 2, - UWB_DBG_CMD_IE_ADD = 3, - UWB_DBG_CMD_IE_RM = 4, - UWB_DBG_CMD_RADIO_START = 5, - UWB_DBG_CMD_RADIO_STOP = 6, -}; - -struct uwb_dbg_cmd_rsv_establish { - __u8 target[6]; - __u8 type; - __u16 max_mas; - __u16 min_mas; - __u8 max_interval; -}; - -struct uwb_dbg_cmd_rsv_terminate { - int index; -}; - -struct uwb_dbg_cmd_ie { - __u8 data[128]; - int len; -}; - -struct uwb_dbg_cmd { - __u32 type; - union { - struct uwb_dbg_cmd_rsv_establish rsv_establish; - struct uwb_dbg_cmd_rsv_terminate rsv_terminate; - struct uwb_dbg_cmd_ie ie_add; - struct uwb_dbg_cmd_ie ie_rm; - }; -}; - -#endif /* #ifndef __LINUX__UWB__DEBUG_CMD_H__ */ diff --git a/drivers/staging/uwb/include/spec.h b/drivers/staging/uwb/include/spec.h deleted file mode 100644 index 5f75caf7b4ba..000000000000 --- a/drivers/staging/uwb/include/spec.h +++ /dev/null @@ -1,767 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Ultra Wide Band - * UWB Standard definitions - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * All these definitions are based on the ECMA-368 standard. - * - * Note all definitions are Little Endian in the wire, and we will - * convert them to host order before operating on the bitfields (that - * yes, we use extensively). - */ - -#ifndef __LINUX__UWB_SPEC_H__ -#define __LINUX__UWB_SPEC_H__ - -#include -#include -#include - -#define i1480_FW 0x00000303 -/* #define i1480_FW 0x00000302 */ - -/** - * Number of Medium Access Slots in a superframe. - * - * UWB divides time in SuperFrames, each one divided in 256 pieces, or - * Medium Access Slots. See MBOA MAC[5.4.5] for details. The MAS is the - * basic bandwidth allocation unit in UWB. - */ -enum { UWB_NUM_MAS = 256 }; - -/** - * Number of Zones in superframe. - * - * UWB divides the superframe into zones with numbering starting from BPST. - * See MBOA MAC[16.8.6] - */ -enum { UWB_NUM_ZONES = 16 }; - -/* - * Number of MAS in a zone. - */ -#define UWB_MAS_PER_ZONE (UWB_NUM_MAS / UWB_NUM_ZONES) - -/* - * Number of MAS required before a row can be considered available. - */ -#define UWB_USABLE_MAS_PER_ROW (UWB_NUM_ZONES - 1) - -/* - * Number of streams per DRP reservation between a pair of devices. - * - * [ECMA-368] section 16.8.6. - */ -enum { UWB_NUM_STREAMS = 8 }; - -/* - * mMasLength - * - * The length of a MAS in microseconds. - * - * [ECMA-368] section 17.16. - */ -enum { UWB_MAS_LENGTH_US = 256 }; - -/* - * mBeaconSlotLength - * - * The length of the beacon slot in microseconds. - * - * [ECMA-368] section 17.16 - */ -enum { UWB_BEACON_SLOT_LENGTH_US = 85 }; - -/* - * mMaxLostBeacons - * - * The number beacons missing in consecutive superframes before a - * device can be considered as unreachable. - * - * [ECMA-368] section 17.16 - */ -enum { UWB_MAX_LOST_BEACONS = 3 }; - -/* - * mDRPBackOffWinMin - * - * The minimum number of superframes to wait before trying to reserve - * extra MAS. - * - * [ECMA-368] section 17.16 - */ -enum { UWB_DRP_BACKOFF_WIN_MIN = 2 }; - -/* - * mDRPBackOffWinMax - * - * The maximum number of superframes to wait before trying to reserve - * extra MAS. - * - * [ECMA-368] section 17.16 - */ -enum { UWB_DRP_BACKOFF_WIN_MAX = 16 }; - -/* - * Length of a superframe in microseconds. - */ -#define UWB_SUPERFRAME_LENGTH_US (UWB_MAS_LENGTH_US * UWB_NUM_MAS) - -/** - * UWB MAC address - * - * It is *imperative* that this struct is exactly 6 packed bytes (as - * it is also used to define headers sent down and up the wire/radio). - */ -struct uwb_mac_addr { - u8 data[ETH_ALEN]; -} __attribute__((packed)); - - -/** - * UWB device address - * - * It is *imperative* that this struct is exactly 6 packed bytes (as - * it is also used to define headers sent down and up the wire/radio). - */ -struct uwb_dev_addr { - u8 data[2]; -} __attribute__((packed)); - - -/** - * Types of UWB addresses - * - * Order matters (by size). - */ -enum uwb_addr_type { - UWB_ADDR_DEV = 0, - UWB_ADDR_MAC = 1, -}; - - -/** Size of a char buffer for printing a MAC/device address */ -enum { UWB_ADDR_STRSIZE = 32 }; - - -/** UWB WiMedia protocol IDs. */ -enum uwb_prid { - UWB_PRID_WLP_RESERVED = 0x0000, - UWB_PRID_WLP = 0x0001, - UWB_PRID_WUSB_BOT = 0x0010, - UWB_PRID_WUSB = 0x0010, - UWB_PRID_WUSB_TOP = 0x001F, -}; - - -/** PHY Rate (MBOA MAC[7.8.12, Table 61]) */ -enum uwb_phy_rate { - UWB_PHY_RATE_53 = 0, - UWB_PHY_RATE_80, - UWB_PHY_RATE_106, - UWB_PHY_RATE_160, - UWB_PHY_RATE_200, - UWB_PHY_RATE_320, - UWB_PHY_RATE_400, - UWB_PHY_RATE_480, - UWB_PHY_RATE_INVALID -}; - - -/** - * Different ways to scan (MBOA MAC[6.2.2, Table 8], WUSB[Table 8-78]) - */ -enum uwb_scan_type { - UWB_SCAN_ONLY = 0, - UWB_SCAN_OUTSIDE_BP, - UWB_SCAN_WHILE_INACTIVE, - UWB_SCAN_DISABLED, - UWB_SCAN_ONLY_STARTTIME, - UWB_SCAN_TOP -}; - - -/** ACK Policy types (MBOA MAC[7.2.1.3]) */ -enum uwb_ack_pol { - UWB_ACK_NO = 0, - UWB_ACK_INM = 1, - UWB_ACK_B = 2, - UWB_ACK_B_REQ = 3, -}; - - -/** DRP reservation types ([ECMA-368 table 106) */ -enum uwb_drp_type { - UWB_DRP_TYPE_ALIEN_BP = 0, - UWB_DRP_TYPE_HARD, - UWB_DRP_TYPE_SOFT, - UWB_DRP_TYPE_PRIVATE, - UWB_DRP_TYPE_PCA, -}; - - -/** DRP Reason Codes ([ECMA-368] table 107) */ -enum uwb_drp_reason { - UWB_DRP_REASON_ACCEPTED = 0, - UWB_DRP_REASON_CONFLICT, - UWB_DRP_REASON_PENDING, - UWB_DRP_REASON_DENIED, - UWB_DRP_REASON_MODIFIED, -}; - -/** Relinquish Request Reason Codes ([ECMA-368] table 113) */ -enum uwb_relinquish_req_reason { - UWB_RELINQUISH_REQ_REASON_NON_SPECIFIC = 0, - UWB_RELINQUISH_REQ_REASON_OVER_ALLOCATION, -}; - -/** - * DRP Notification Reason Codes (WHCI 0.95 [3.1.4.9]) - */ -enum uwb_drp_notif_reason { - UWB_DRP_NOTIF_DRP_IE_RCVD = 0, - UWB_DRP_NOTIF_CONFLICT, - UWB_DRP_NOTIF_TERMINATE, -}; - - -/** Allocation of MAS slots in a DRP request MBOA MAC[7.8.7] */ -struct uwb_drp_alloc { - __le16 zone_bm; - __le16 mas_bm; -} __attribute__((packed)); - - -/** General MAC Header format (ECMA-368[16.2]) */ -struct uwb_mac_frame_hdr { - __le16 Frame_Control; - struct uwb_dev_addr DestAddr; - struct uwb_dev_addr SrcAddr; - __le16 Sequence_Control; - __le16 Access_Information; -} __attribute__((packed)); - - -/** - * uwb_beacon_frame - a beacon frame including MAC headers - * - * [ECMA] section 16.3. - */ -struct uwb_beacon_frame { - struct uwb_mac_frame_hdr hdr; - struct uwb_mac_addr Device_Identifier; /* may be a NULL EUI-48 */ - u8 Beacon_Slot_Number; - u8 Device_Control; - u8 IEData[]; -} __attribute__((packed)); - - -/** Information Element codes (MBOA MAC[T54]) */ -enum uwb_ie { - UWB_PCA_AVAILABILITY = 2, - UWB_IE_DRP_AVAILABILITY = 8, - UWB_IE_DRP = 9, - UWB_BP_SWITCH_IE = 11, - UWB_MAC_CAPABILITIES_IE = 12, - UWB_PHY_CAPABILITIES_IE = 13, - UWB_APP_SPEC_PROBE_IE = 15, - UWB_IDENTIFICATION_IE = 19, - UWB_MASTER_KEY_ID_IE = 20, - UWB_RELINQUISH_REQUEST_IE = 21, - UWB_IE_WLP = 250, /* WiMedia Logical Link Control Protocol WLP 0.99 */ - UWB_APP_SPEC_IE = 255, -}; - - -/** - * Header common to all Information Elements (IEs) - */ -struct uwb_ie_hdr { - u8 element_id; /* enum uwb_ie */ - u8 length; -} __attribute__((packed)); - - -/** Dynamic Reservation Protocol IE (MBOA MAC[7.8.6]) */ -struct uwb_ie_drp { - struct uwb_ie_hdr hdr; - __le16 drp_control; - struct uwb_dev_addr dev_addr; - struct uwb_drp_alloc allocs[]; -} __attribute__((packed)); - -static inline int uwb_ie_drp_type(struct uwb_ie_drp *ie) -{ - return (le16_to_cpu(ie->drp_control) >> 0) & 0x7; -} - -static inline int uwb_ie_drp_stream_index(struct uwb_ie_drp *ie) -{ - return (le16_to_cpu(ie->drp_control) >> 3) & 0x7; -} - -static inline int uwb_ie_drp_reason_code(struct uwb_ie_drp *ie) -{ - return (le16_to_cpu(ie->drp_control) >> 6) & 0x7; -} - -static inline int uwb_ie_drp_status(struct uwb_ie_drp *ie) -{ - return (le16_to_cpu(ie->drp_control) >> 9) & 0x1; -} - -static inline int uwb_ie_drp_owner(struct uwb_ie_drp *ie) -{ - return (le16_to_cpu(ie->drp_control) >> 10) & 0x1; -} - -static inline int uwb_ie_drp_tiebreaker(struct uwb_ie_drp *ie) -{ - return (le16_to_cpu(ie->drp_control) >> 11) & 0x1; -} - -static inline int uwb_ie_drp_unsafe(struct uwb_ie_drp *ie) -{ - return (le16_to_cpu(ie->drp_control) >> 12) & 0x1; -} - -static inline void uwb_ie_drp_set_type(struct uwb_ie_drp *ie, enum uwb_drp_type type) -{ - u16 drp_control = le16_to_cpu(ie->drp_control); - drp_control = (drp_control & ~(0x7 << 0)) | (type << 0); - ie->drp_control = cpu_to_le16(drp_control); -} - -static inline void uwb_ie_drp_set_stream_index(struct uwb_ie_drp *ie, int stream_index) -{ - u16 drp_control = le16_to_cpu(ie->drp_control); - drp_control = (drp_control & ~(0x7 << 3)) | (stream_index << 3); - ie->drp_control = cpu_to_le16(drp_control); -} - -static inline void uwb_ie_drp_set_reason_code(struct uwb_ie_drp *ie, - enum uwb_drp_reason reason_code) -{ - u16 drp_control = le16_to_cpu(ie->drp_control); - drp_control = (ie->drp_control & ~(0x7 << 6)) | (reason_code << 6); - ie->drp_control = cpu_to_le16(drp_control); -} - -static inline void uwb_ie_drp_set_status(struct uwb_ie_drp *ie, int status) -{ - u16 drp_control = le16_to_cpu(ie->drp_control); - drp_control = (drp_control & ~(0x1 << 9)) | (status << 9); - ie->drp_control = cpu_to_le16(drp_control); -} - -static inline void uwb_ie_drp_set_owner(struct uwb_ie_drp *ie, int owner) -{ - u16 drp_control = le16_to_cpu(ie->drp_control); - drp_control = (drp_control & ~(0x1 << 10)) | (owner << 10); - ie->drp_control = cpu_to_le16(drp_control); -} - -static inline void uwb_ie_drp_set_tiebreaker(struct uwb_ie_drp *ie, int tiebreaker) -{ - u16 drp_control = le16_to_cpu(ie->drp_control); - drp_control = (drp_control & ~(0x1 << 11)) | (tiebreaker << 11); - ie->drp_control = cpu_to_le16(drp_control); -} - -static inline void uwb_ie_drp_set_unsafe(struct uwb_ie_drp *ie, int unsafe) -{ - u16 drp_control = le16_to_cpu(ie->drp_control); - drp_control = (drp_control & ~(0x1 << 12)) | (unsafe << 12); - ie->drp_control = cpu_to_le16(drp_control); -} - -/** Dynamic Reservation Protocol IE (MBOA MAC[7.8.7]) */ -struct uwb_ie_drp_avail { - struct uwb_ie_hdr hdr; - DECLARE_BITMAP(bmp, UWB_NUM_MAS); -} __attribute__((packed)); - -/* Relinqish Request IE ([ECMA-368] section 16.8.19). */ -struct uwb_relinquish_request_ie { - struct uwb_ie_hdr hdr; - __le16 relinquish_req_control; - struct uwb_dev_addr dev_addr; - struct uwb_drp_alloc allocs[]; -} __attribute__((packed)); - -static inline int uwb_ie_relinquish_req_reason_code(struct uwb_relinquish_request_ie *ie) -{ - return (le16_to_cpu(ie->relinquish_req_control) >> 0) & 0xf; -} - -static inline void uwb_ie_relinquish_req_set_reason_code(struct uwb_relinquish_request_ie *ie, - int reason_code) -{ - u16 ctrl = le16_to_cpu(ie->relinquish_req_control); - ctrl = (ctrl & ~(0xf << 0)) | (reason_code << 0); - ie->relinquish_req_control = cpu_to_le16(ctrl); -} - -/** - * The Vendor ID is set to an OUI that indicates the vendor of the device. - * ECMA-368 [16.8.10] - */ -struct uwb_vendor_id { - u8 data[3]; -} __attribute__((packed)); - -/** - * The device type ID - * FIXME: clarify what this means - * ECMA-368 [16.8.10] - */ -struct uwb_device_type_id { - u8 data[3]; -} __attribute__((packed)); - - -/** - * UWB device information types - * ECMA-368 [16.8.10] - */ -enum uwb_dev_info_type { - UWB_DEV_INFO_VENDOR_ID = 0, - UWB_DEV_INFO_VENDOR_TYPE, - UWB_DEV_INFO_NAME, -}; - -/** - * UWB device information found in Identification IE - * ECMA-368 [16.8.10] - */ -struct uwb_dev_info { - u8 type; /* enum uwb_dev_info_type */ - u8 length; - u8 data[]; -} __attribute__((packed)); - -/** - * UWB Identification IE - * ECMA-368 [16.8.10] - */ -struct uwb_identification_ie { - struct uwb_ie_hdr hdr; - struct uwb_dev_info info[]; -} __attribute__((packed)); - -/* - * UWB Radio Controller - * - * These definitions are common to the Radio Control layers as - * exported by the WUSB1.0 HWA and WHCI interfaces. - */ - -/** Radio Control Command Block (WUSB1.0[Table 8-65] and WHCI 0.95) */ -struct uwb_rccb { - u8 bCommandType; /* enum hwa_cet */ - __le16 wCommand; /* Command code */ - u8 bCommandContext; /* Context ID */ -} __attribute__((packed)); - - -/** Radio Control Event Block (WUSB[table 8-66], WHCI 0.95) */ -struct uwb_rceb { - u8 bEventType; /* enum hwa_cet */ - __le16 wEvent; /* Event code */ - u8 bEventContext; /* Context ID */ -} __attribute__((packed)); - - -enum { - UWB_RC_CET_GENERAL = 0, /* General Command/Event type */ - UWB_RC_CET_EX_TYPE_1 = 1, /* Extended Type 1 Command/Event type */ -}; - -/* Commands to the radio controller */ -enum uwb_rc_cmd { - UWB_RC_CMD_CHANNEL_CHANGE = 16, - UWB_RC_CMD_DEV_ADDR_MGMT = 17, /* Device Address Management */ - UWB_RC_CMD_GET_IE = 18, /* GET Information Elements */ - UWB_RC_CMD_RESET = 19, - UWB_RC_CMD_SCAN = 20, /* Scan management */ - UWB_RC_CMD_SET_BEACON_FILTER = 21, - UWB_RC_CMD_SET_DRP_IE = 22, /* Dynamic Reservation Protocol IEs */ - UWB_RC_CMD_SET_IE = 23, /* Information Element management */ - UWB_RC_CMD_SET_NOTIFICATION_FILTER = 24, - UWB_RC_CMD_SET_TX_POWER = 25, - UWB_RC_CMD_SLEEP = 26, - UWB_RC_CMD_START_BEACON = 27, - UWB_RC_CMD_STOP_BEACON = 28, - UWB_RC_CMD_BP_MERGE = 29, - UWB_RC_CMD_SEND_COMMAND_FRAME = 30, - UWB_RC_CMD_SET_ASIE_NOTIF = 31, -}; - -/* Notifications from the radio controller */ -enum uwb_rc_evt { - UWB_RC_EVT_IE_RCV = 0, - UWB_RC_EVT_BEACON = 1, - UWB_RC_EVT_BEACON_SIZE = 2, - UWB_RC_EVT_BPOIE_CHANGE = 3, - UWB_RC_EVT_BP_SLOT_CHANGE = 4, - UWB_RC_EVT_BP_SWITCH_IE_RCV = 5, - UWB_RC_EVT_DEV_ADDR_CONFLICT = 6, - UWB_RC_EVT_DRP_AVAIL = 7, - UWB_RC_EVT_DRP = 8, - UWB_RC_EVT_BP_SWITCH_STATUS = 9, - UWB_RC_EVT_CMD_FRAME_RCV = 10, - UWB_RC_EVT_CHANNEL_CHANGE_IE_RCV = 11, - /* Events (command responses) use the same code as the command */ - UWB_RC_EVT_UNKNOWN_CMD_RCV = 65535, -}; - -enum uwb_rc_extended_type_1_cmd { - UWB_RC_SET_DAA_ENERGY_MASK = 32, - UWB_RC_SET_NOTIFICATION_FILTER_EX = 33, -}; - -enum uwb_rc_extended_type_1_evt { - UWB_RC_DAA_ENERGY_DETECTED = 0, -}; - -/* Radio Control Result Code. [WHCI] table 3-3. */ -enum { - UWB_RC_RES_SUCCESS = 0, - UWB_RC_RES_FAIL, - UWB_RC_RES_FAIL_HARDWARE, - UWB_RC_RES_FAIL_NO_SLOTS, - UWB_RC_RES_FAIL_BEACON_TOO_LARGE, - UWB_RC_RES_FAIL_INVALID_PARAMETER, - UWB_RC_RES_FAIL_UNSUPPORTED_PWR_LEVEL, - UWB_RC_RES_FAIL_INVALID_IE_DATA, - UWB_RC_RES_FAIL_BEACON_SIZE_EXCEEDED, - UWB_RC_RES_FAIL_CANCELLED, - UWB_RC_RES_FAIL_INVALID_STATE, - UWB_RC_RES_FAIL_INVALID_SIZE, - UWB_RC_RES_FAIL_ACK_NOT_RECEIVED, - UWB_RC_RES_FAIL_NO_MORE_ASIE_NOTIF, - UWB_RC_RES_FAIL_TIME_OUT = 255, -}; - -/* Confirm event. [WHCI] section 3.1.3.1 etc. */ -struct uwb_rc_evt_confirm { - struct uwb_rceb rceb; - u8 bResultCode; -} __attribute__((packed)); - -/* Device Address Management event. [WHCI] section 3.1.3.2. */ -struct uwb_rc_evt_dev_addr_mgmt { - struct uwb_rceb rceb; - u8 baAddr[ETH_ALEN]; - u8 bResultCode; -} __attribute__((packed)); - - -/* Get IE Event. [WHCI] section 3.1.3.3. */ -struct uwb_rc_evt_get_ie { - struct uwb_rceb rceb; - __le16 wIELength; - u8 IEData[]; -} __attribute__((packed)); - -/* Set DRP IE Event. [WHCI] section 3.1.3.7. */ -struct uwb_rc_evt_set_drp_ie { - struct uwb_rceb rceb; - __le16 wRemainingSpace; - u8 bResultCode; -} __attribute__((packed)); - -/* Set IE Event. [WHCI] section 3.1.3.8. */ -struct uwb_rc_evt_set_ie { - struct uwb_rceb rceb; - __le16 RemainingSpace; - u8 bResultCode; -} __attribute__((packed)); - -/* Scan command. [WHCI] 3.1.3.5. */ -struct uwb_rc_cmd_scan { - struct uwb_rccb rccb; - u8 bChannelNumber; - u8 bScanState; - __le16 wStartTime; -} __attribute__((packed)); - -/* Set DRP IE command. [WHCI] section 3.1.3.7. */ -struct uwb_rc_cmd_set_drp_ie { - struct uwb_rccb rccb; - __le16 wIELength; - struct uwb_ie_drp IEData[]; -} __attribute__((packed)); - -/* Set IE command. [WHCI] section 3.1.3.8. */ -struct uwb_rc_cmd_set_ie { - struct uwb_rccb rccb; - __le16 wIELength; - u8 IEData[]; -} __attribute__((packed)); - -/* Set DAA Energy Mask event. [WHCI 0.96] section 3.1.3.17. */ -struct uwb_rc_evt_set_daa_energy_mask { - struct uwb_rceb rceb; - __le16 wLength; - u8 result; -} __attribute__((packed)); - -/* Set Notification Filter Extended event. [WHCI 0.96] section 3.1.3.18. */ -struct uwb_rc_evt_set_notification_filter_ex { - struct uwb_rceb rceb; - __le16 wLength; - u8 result; -} __attribute__((packed)); - -/* IE Received notification. [WHCI] section 3.1.4.1. */ -struct uwb_rc_evt_ie_rcv { - struct uwb_rceb rceb; - struct uwb_dev_addr SrcAddr; - __le16 wIELength; - u8 IEData[]; -} __attribute__((packed)); - -/* Type of the received beacon. [WHCI] section 3.1.4.2. */ -enum uwb_rc_beacon_type { - UWB_RC_BEACON_TYPE_SCAN = 0, - UWB_RC_BEACON_TYPE_NEIGHBOR, - UWB_RC_BEACON_TYPE_OL_ALIEN, - UWB_RC_BEACON_TYPE_NOL_ALIEN, -}; - -/* Beacon received notification. [WHCI] 3.1.4.2. */ -struct uwb_rc_evt_beacon { - struct uwb_rceb rceb; - u8 bChannelNumber; - u8 bBeaconType; - __le16 wBPSTOffset; - u8 bLQI; - u8 bRSSI; - __le16 wBeaconInfoLength; - u8 BeaconInfo[]; -} __attribute__((packed)); - - -/* Beacon Size Change notification. [WHCI] section 3.1.4.3 */ -struct uwb_rc_evt_beacon_size { - struct uwb_rceb rceb; - __le16 wNewBeaconSize; -} __attribute__((packed)); - - -/* BPOIE Change notification. [WHCI] section 3.1.4.4. */ -struct uwb_rc_evt_bpoie_change { - struct uwb_rceb rceb; - __le16 wBPOIELength; - u8 BPOIE[]; -} __attribute__((packed)); - - -/* Beacon Slot Change notification. [WHCI] section 3.1.4.5. */ -struct uwb_rc_evt_bp_slot_change { - struct uwb_rceb rceb; - u8 slot_info; -} __attribute__((packed)); - -static inline int uwb_rc_evt_bp_slot_change_slot_num( - const struct uwb_rc_evt_bp_slot_change *evt) -{ - return evt->slot_info & 0x7f; -} - -static inline int uwb_rc_evt_bp_slot_change_no_slot( - const struct uwb_rc_evt_bp_slot_change *evt) -{ - return (evt->slot_info & 0x80) >> 7; -} - -/* BP Switch IE Received notification. [WHCI] section 3.1.4.6. */ -struct uwb_rc_evt_bp_switch_ie_rcv { - struct uwb_rceb rceb; - struct uwb_dev_addr wSrcAddr; - __le16 wIELength; - u8 IEData[]; -} __attribute__((packed)); - -/* DevAddr Conflict notification. [WHCI] section 3.1.4.7. */ -struct uwb_rc_evt_dev_addr_conflict { - struct uwb_rceb rceb; -} __attribute__((packed)); - -/* DRP notification. [WHCI] section 3.1.4.9. */ -struct uwb_rc_evt_drp { - struct uwb_rceb rceb; - struct uwb_dev_addr src_addr; - u8 reason; - u8 beacon_slot_number; - __le16 ie_length; - u8 ie_data[]; -} __attribute__((packed)); - -static inline enum uwb_drp_notif_reason uwb_rc_evt_drp_reason(struct uwb_rc_evt_drp *evt) -{ - return evt->reason & 0x0f; -} - - -/* DRP Availability Change notification. [WHCI] section 3.1.4.8. */ -struct uwb_rc_evt_drp_avail { - struct uwb_rceb rceb; - DECLARE_BITMAP(bmp, UWB_NUM_MAS); -} __attribute__((packed)); - -/* BP switch status notification. [WHCI] section 3.1.4.10. */ -struct uwb_rc_evt_bp_switch_status { - struct uwb_rceb rceb; - u8 status; - u8 slot_offset; - __le16 bpst_offset; - u8 move_countdown; -} __attribute__((packed)); - -/* Command Frame Received notification. [WHCI] section 3.1.4.11. */ -struct uwb_rc_evt_cmd_frame_rcv { - struct uwb_rceb rceb; - __le16 receive_time; - struct uwb_dev_addr wSrcAddr; - struct uwb_dev_addr wDstAddr; - __le16 control; - __le16 reserved; - __le16 dataLength; - u8 data[]; -} __attribute__((packed)); - -/* Channel Change IE Received notification. [WHCI] section 3.1.4.12. */ -struct uwb_rc_evt_channel_change_ie_rcv { - struct uwb_rceb rceb; - struct uwb_dev_addr wSrcAddr; - __le16 wIELength; - u8 IEData[]; -} __attribute__((packed)); - -/* DAA Energy Detected notification. [WHCI 0.96] section 3.1.4.14. */ -struct uwb_rc_evt_daa_energy_detected { - struct uwb_rceb rceb; - __le16 wLength; - u8 bandID; - u8 reserved; - u8 toneBmp[16]; -} __attribute__((packed)); - - -/** - * Radio Control Interface Class Descriptor - * - * WUSB 1.0 [8.6.1.2] - */ -struct uwb_rc_control_intf_class_desc { - u8 bLength; - u8 bDescriptorType; - __le16 bcdRCIVersion; -} __attribute__((packed)); - -#endif /* #ifndef __LINUX__UWB_SPEC_H__ */ diff --git a/drivers/staging/uwb/include/umc.h b/drivers/staging/uwb/include/umc.h deleted file mode 100644 index ddbceb39ad15..000000000000 --- a/drivers/staging/uwb/include/umc.h +++ /dev/null @@ -1,192 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * UWB Multi-interface Controller support. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - * - * UMC (UWB Multi-interface Controller) capabilities (e.g., radio - * controller, host controller) are presented as devices on the "umc" - * bus. - * - * The radio controller is not strictly a UMC capability but it's - * useful to present it as such. - * - * References: - * - * [WHCI] Wireless Host Controller Interface Specification for - * Certified Wireless Universal Serial Bus, revision 0.95. - * - * How this works is kind of convoluted but simple. The whci.ko driver - * loads when WHCI devices are detected. These WHCI devices expose - * many devices in the same PCI function (they couldn't have reused - * functions, no), so for each PCI function that exposes these many - * devices, whci ceates a umc_dev [whci_probe() -> whci_add_cap()] - * with umc_device_create() and adds it to the bus with - * umc_device_register(). - * - * umc_device_register() calls device_register() which will push the - * bus management code to load your UMC driver's somehting_probe() - * that you have registered for that capability code. - * - * Now when the WHCI device is removed, whci_remove() will go over - * each umc_dev assigned to each of the PCI function's capabilities - * and through whci_del_cap() call umc_device_unregister() each - * created umc_dev. Of course, if you are bound to the device, your - * driver's something_remove() will be called. - */ - -#ifndef _LINUX_UWB_UMC_H_ -#define _LINUX_UWB_UMC_H_ - -#include -#include - -/* - * UMC capability IDs. - * - * 0x00 is reserved so use it for the radio controller device. - * - * [WHCI] table 2-8 - */ -#define UMC_CAP_ID_WHCI_RC 0x00 /* radio controller */ -#define UMC_CAP_ID_WHCI_WUSB_HC 0x01 /* WUSB host controller */ - -/** - * struct umc_dev - UMC capability device - * - * @version: version of the specification this capability conforms to. - * @cap_id: capability ID. - * @bar: PCI Bar (64 bit) where the resource lies - * @resource: register space resource. - * @irq: interrupt line. - */ -struct umc_dev { - u16 version; - u8 cap_id; - u8 bar; - struct resource resource; - unsigned irq; - struct device dev; -}; - -#define to_umc_dev(d) container_of(d, struct umc_dev, dev) - -/** - * struct umc_driver - UMC capability driver - * @cap_id: supported capability ID. - * @match: driver specific capability matching function. - * @match_data: driver specific data for match() (e.g., a - * table of pci_device_id's if umc_match_pci_id() is used). - */ -struct umc_driver { - char *name; - u8 cap_id; - int (*match)(struct umc_driver *, struct umc_dev *); - const void *match_data; - - int (*probe)(struct umc_dev *); - void (*remove)(struct umc_dev *); - int (*pre_reset)(struct umc_dev *); - int (*post_reset)(struct umc_dev *); - - struct device_driver driver; -}; - -#define to_umc_driver(d) container_of(d, struct umc_driver, driver) - -extern struct bus_type umc_bus_type; - -struct umc_dev *umc_device_create(struct device *parent, int n); -int __must_check umc_device_register(struct umc_dev *umc); -void umc_device_unregister(struct umc_dev *umc); - -int __must_check __umc_driver_register(struct umc_driver *umc_drv, - struct module *mod, - const char *mod_name); - -/** - * umc_driver_register - register a UMC capabiltity driver. - * @umc_drv: pointer to the driver. - */ -#define umc_driver_register(umc_drv) \ - __umc_driver_register(umc_drv, THIS_MODULE, KBUILD_MODNAME) - -void umc_driver_unregister(struct umc_driver *umc_drv); - -/* - * Utility function you can use to match (umc_driver->match) against a - * null-terminated array of 'struct pci_device_id' in - * umc_driver->match_data. - */ -int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc); - -/** - * umc_parent_pci_dev - return the UMC's parent PCI device or NULL if none - * @umc_dev: UMC device whose parent PCI device we are looking for - * - * DIRTY!!! DON'T RELY ON THIS - * - * FIXME: This is as dirty as it gets, but we need some way to check - * the correct type of umc_dev->parent (so that for example, we can - * cast to pci_dev). Casting to pci_dev is necessary because at some - * point we need to request resources from the device. Mapping is - * easily over come (ioremap and stuff are bus agnostic), but hooking - * up to some error handlers (such as pci error handlers) might need - * this. - * - * THIS might (probably will) be removed in the future, so don't count - * on it. - */ -static inline struct pci_dev *umc_parent_pci_dev(struct umc_dev *umc_dev) -{ - struct pci_dev *pci_dev = NULL; - if (dev_is_pci(umc_dev->dev.parent)) - pci_dev = to_pci_dev(umc_dev->dev.parent); - return pci_dev; -} - -/** - * umc_dev_get() - reference a UMC device. - * @umc_dev: Pointer to UMC device. - * - * NOTE: we are assuming in this whole scheme that the parent device - * is referenced at _probe() time and unreferenced at _remove() - * time by the parent's subsystem. - */ -static inline struct umc_dev *umc_dev_get(struct umc_dev *umc_dev) -{ - get_device(&umc_dev->dev); - return umc_dev; -} - -/** - * umc_dev_put() - unreference a UMC device. - * @umc_dev: Pointer to UMC device. - */ -static inline void umc_dev_put(struct umc_dev *umc_dev) -{ - put_device(&umc_dev->dev); -} - -/** - * umc_set_drvdata - set UMC device's driver data. - * @umc_dev: Pointer to UMC device. - * @data: Data to set. - */ -static inline void umc_set_drvdata(struct umc_dev *umc_dev, void *data) -{ - dev_set_drvdata(&umc_dev->dev, data); -} - -/** - * umc_get_drvdata - recover UMC device's driver data. - * @umc_dev: Pointer to UMC device. - */ -static inline void *umc_get_drvdata(struct umc_dev *umc_dev) -{ - return dev_get_drvdata(&umc_dev->dev); -} - -int umc_controller_reset(struct umc_dev *umc); - -#endif /* #ifndef _LINUX_UWB_UMC_H_ */ diff --git a/drivers/staging/uwb/include/whci.h b/drivers/staging/uwb/include/whci.h deleted file mode 100644 index 1a5c2cc2b008..000000000000 --- a/drivers/staging/uwb/include/whci.h +++ /dev/null @@ -1,102 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Wireless Host Controller Interface for Ultra-Wide-Band and Wireless USB - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * References: - * [WHCI] Wireless Host Controller Interface Specification for - * Certified Wireless Universal Serial Bus, revision 0.95. - */ -#ifndef _LINUX_UWB_WHCI_H_ -#define _LINUX_UWB_WHCI_H_ - -#include - -/* - * UWB interface capability registers (offsets from UWBBASE) - * - * [WHCI] section 2.2 - */ -#define UWBCAPINFO 0x00 /* == UWBCAPDATA(0) */ -# define UWBCAPINFO_TO_N_CAPS(c) (((c) >> 0) & 0xFull) -#define UWBCAPDATA(n) (8*(n)) -# define UWBCAPDATA_TO_VERSION(c) (((c) >> 32) & 0xFFFFull) -# define UWBCAPDATA_TO_OFFSET(c) (((c) >> 18) & 0x3FFFull) -# define UWBCAPDATA_TO_BAR(c) (((c) >> 16) & 0x3ull) -# define UWBCAPDATA_TO_SIZE(c) ((((c) >> 8) & 0xFFull) * sizeof(u32)) -# define UWBCAPDATA_TO_CAP_ID(c) (((c) >> 0) & 0xFFull) - -/* Size of the WHCI capability data (including the RC capability) for - a device with n capabilities. */ -#define UWBCAPDATA_SIZE(n) (8 + 8*(n)) - - -/* - * URC registers (offsets from URCBASE) - * - * [WHCI] section 2.3 - */ -#define URCCMD 0x00 -# define URCCMD_RESET (1 << 31) /* UMC Hardware reset */ -# define URCCMD_RS (1 << 30) /* Run/Stop */ -# define URCCMD_EARV (1 << 29) /* Event Address Register Valid */ -# define URCCMD_ACTIVE (1 << 15) /* Command is active */ -# define URCCMD_IWR (1 << 14) /* Interrupt When Ready */ -# define URCCMD_SIZE_MASK 0x00000fff /* Command size mask */ -#define URCSTS 0x04 -# define URCSTS_EPS (1 << 17) /* Event Processing Status */ -# define URCSTS_HALTED (1 << 16) /* RC halted */ -# define URCSTS_HSE (1 << 10) /* Host System Error...fried */ -# define URCSTS_ER (1 << 9) /* Event Ready */ -# define URCSTS_RCI (1 << 8) /* Ready for Command Interrupt */ -# define URCSTS_INT_MASK 0x00000700 /* URC interrupt sources */ -# define URCSTS_ISI 0x000000ff /* Interrupt Source Identification */ -#define URCINTR 0x08 -# define URCINTR_EN_ALL 0x000007ff /* Enable all interrupt sources */ -#define URCCMDADDR 0x10 -#define URCEVTADDR 0x18 -# define URCEVTADDR_OFFSET_MASK 0xfff /* Event pointer offset mask */ - - -/** Write 32 bit @value to little endian register at @addr */ -static inline -void le_writel(u32 value, void __iomem *addr) -{ - iowrite32(value, addr); -} - - -/** Read from 32 bit little endian register at @addr */ -static inline -u32 le_readl(void __iomem *addr) -{ - return ioread32(addr); -} - - -/** Write 64 bit @value to little endian register at @addr */ -static inline -void le_writeq(u64 value, void __iomem *addr) -{ - iowrite32(value, addr); - iowrite32(value >> 32, addr + 4); -} - - -/** Read from 64 bit little endian register at @addr */ -static inline -u64 le_readq(void __iomem *addr) -{ - u64 value; - value = ioread32(addr); - value |= (u64)ioread32(addr + 4) << 32; - return value; -} - -extern int whci_wait_for(struct device *dev, u32 __iomem *reg, - u32 mask, u32 result, - unsigned long max_ms, const char *tag); - -#endif /* #ifndef _LINUX_UWB_WHCI_H_ */ diff --git a/drivers/staging/uwb/lc-dev.c b/drivers/staging/uwb/lc-dev.c deleted file mode 100644 index 3e5c07fd6b10..000000000000 --- a/drivers/staging/uwb/lc-dev.c +++ /dev/null @@ -1,457 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Life cycle of devices - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "uwb-internal.h" - -/* We initialize addresses to 0xff (invalid, as it is bcast) */ -static inline void uwb_dev_addr_init(struct uwb_dev_addr *addr) -{ - memset(&addr->data, 0xff, sizeof(addr->data)); -} - -static inline void uwb_mac_addr_init(struct uwb_mac_addr *addr) -{ - memset(&addr->data, 0xff, sizeof(addr->data)); -} - -/* - * Add callback @new to be called when an event occurs in @rc. - */ -int uwb_notifs_register(struct uwb_rc *rc, struct uwb_notifs_handler *new) -{ - if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) - return -ERESTARTSYS; - list_add(&new->list_node, &rc->notifs_chain.list); - mutex_unlock(&rc->notifs_chain.mutex); - return 0; -} -EXPORT_SYMBOL_GPL(uwb_notifs_register); - -/* - * Remove event handler (callback) - */ -int uwb_notifs_deregister(struct uwb_rc *rc, struct uwb_notifs_handler *entry) -{ - if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) - return -ERESTARTSYS; - list_del(&entry->list_node); - mutex_unlock(&rc->notifs_chain.mutex); - return 0; -} -EXPORT_SYMBOL_GPL(uwb_notifs_deregister); - -/* - * Notify all event handlers of a given event on @rc - * - * We are called with a valid reference to the device, or NULL if the - * event is not for a particular event (e.g., a BG join event). - */ -void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event) -{ - struct uwb_notifs_handler *handler; - if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) - return; - if (!list_empty(&rc->notifs_chain.list)) { - list_for_each_entry(handler, &rc->notifs_chain.list, list_node) { - handler->cb(handler->data, uwb_dev, event); - } - } - mutex_unlock(&rc->notifs_chain.mutex); -} - -/* - * Release the backing device of a uwb_dev that has been dynamically allocated. - */ -static void uwb_dev_sys_release(struct device *dev) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - - uwb_bce_put(uwb_dev->bce); - memset(uwb_dev, 0x69, sizeof(*uwb_dev)); - kfree(uwb_dev); -} - -/* - * Initialize a UWB device instance - * - * Alloc, zero and call this function. - */ -void uwb_dev_init(struct uwb_dev *uwb_dev) -{ - mutex_init(&uwb_dev->mutex); - device_initialize(&uwb_dev->dev); - uwb_dev->dev.release = uwb_dev_sys_release; - uwb_dev_addr_init(&uwb_dev->dev_addr); - uwb_mac_addr_init(&uwb_dev->mac_addr); - bitmap_fill(uwb_dev->streams, UWB_NUM_GLOBAL_STREAMS); -} - -static ssize_t uwb_dev_EUI_48_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - char addr[UWB_ADDR_STRSIZE]; - - uwb_mac_addr_print(addr, sizeof(addr), &uwb_dev->mac_addr); - return sprintf(buf, "%s\n", addr); -} -static DEVICE_ATTR(EUI_48, S_IRUGO, uwb_dev_EUI_48_show, NULL); - -static ssize_t uwb_dev_DevAddr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - char addr[UWB_ADDR_STRSIZE]; - - uwb_dev_addr_print(addr, sizeof(addr), &uwb_dev->dev_addr); - return sprintf(buf, "%s\n", addr); -} -static DEVICE_ATTR(DevAddr, S_IRUGO, uwb_dev_DevAddr_show, NULL); - -/* - * Show the BPST of this device. - * - * Calculated from the receive time of the device's beacon and it's - * slot number. - */ -static ssize_t uwb_dev_BPST_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_beca_e *bce; - struct uwb_beacon_frame *bf; - u16 bpst; - - bce = uwb_dev->bce; - mutex_lock(&bce->mutex); - bf = (struct uwb_beacon_frame *)bce->be->BeaconInfo; - bpst = bce->be->wBPSTOffset - - (u16)(bf->Beacon_Slot_Number * UWB_BEACON_SLOT_LENGTH_US); - mutex_unlock(&bce->mutex); - - return sprintf(buf, "%d\n", bpst); -} -static DEVICE_ATTR(BPST, S_IRUGO, uwb_dev_BPST_show, NULL); - -/* - * Show the IEs a device is beaconing - * - * We need to access the beacon cache, so we just lock it really - * quick, print the IEs and unlock. - * - * We have a reference on the cache entry, so that should be - * quite safe. - */ -static ssize_t uwb_dev_IEs_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - - return uwb_bce_print_IEs(uwb_dev, uwb_dev->bce, buf, PAGE_SIZE); -} -static DEVICE_ATTR(IEs, S_IRUGO | S_IWUSR, uwb_dev_IEs_show, NULL); - -static ssize_t uwb_dev_LQE_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_beca_e *bce = uwb_dev->bce; - size_t result; - - mutex_lock(&bce->mutex); - result = stats_show(&uwb_dev->bce->lqe_stats, buf); - mutex_unlock(&bce->mutex); - return result; -} - -static ssize_t uwb_dev_LQE_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_beca_e *bce = uwb_dev->bce; - ssize_t result; - - mutex_lock(&bce->mutex); - result = stats_store(&uwb_dev->bce->lqe_stats, buf, size); - mutex_unlock(&bce->mutex); - return result; -} -static DEVICE_ATTR(LQE, S_IRUGO | S_IWUSR, uwb_dev_LQE_show, uwb_dev_LQE_store); - -static ssize_t uwb_dev_RSSI_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_beca_e *bce = uwb_dev->bce; - size_t result; - - mutex_lock(&bce->mutex); - result = stats_show(&uwb_dev->bce->rssi_stats, buf); - mutex_unlock(&bce->mutex); - return result; -} - -static ssize_t uwb_dev_RSSI_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_beca_e *bce = uwb_dev->bce; - ssize_t result; - - mutex_lock(&bce->mutex); - result = stats_store(&uwb_dev->bce->rssi_stats, buf, size); - mutex_unlock(&bce->mutex); - return result; -} -static DEVICE_ATTR(RSSI, S_IRUGO | S_IWUSR, uwb_dev_RSSI_show, uwb_dev_RSSI_store); - - -static struct attribute *uwb_dev_attrs[] = { - &dev_attr_EUI_48.attr, - &dev_attr_DevAddr.attr, - &dev_attr_BPST.attr, - &dev_attr_IEs.attr, - &dev_attr_LQE.attr, - &dev_attr_RSSI.attr, - NULL, -}; -ATTRIBUTE_GROUPS(uwb_dev); - -/* UWB bus type. */ -struct bus_type uwb_bus_type = { - .name = "uwb", - .dev_groups = uwb_dev_groups, -}; - -/** - * Device SYSFS registration - */ -static int __uwb_dev_sys_add(struct uwb_dev *uwb_dev, struct device *parent_dev) -{ - struct device *dev; - - dev = &uwb_dev->dev; - dev->parent = parent_dev; - dev_set_drvdata(dev, uwb_dev); - - return device_add(dev); -} - - -static void __uwb_dev_sys_rm(struct uwb_dev *uwb_dev) -{ - dev_set_drvdata(&uwb_dev->dev, NULL); - device_del(&uwb_dev->dev); -} - - -/** - * Register and initialize a new UWB device - * - * Did you call uwb_dev_init() on it? - * - * @parent_rc: is the parent radio controller who has the link to the - * device. When registering the UWB device that is a UWB - * Radio Controller, we point back to it. - * - * If registering the device that is part of a radio, caller has set - * rc->uwb_dev->dev. Otherwise it is to be left NULL--a new one will - * be allocated. - */ -int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev, - struct uwb_rc *parent_rc) -{ - int result; - struct device *dev; - - BUG_ON(uwb_dev == NULL); - BUG_ON(parent_dev == NULL); - BUG_ON(parent_rc == NULL); - - mutex_lock(&uwb_dev->mutex); - dev = &uwb_dev->dev; - uwb_dev->rc = parent_rc; - result = __uwb_dev_sys_add(uwb_dev, parent_dev); - if (result < 0) - printk(KERN_ERR "UWB: unable to register dev %s with sysfs: %d\n", - dev_name(dev), result); - mutex_unlock(&uwb_dev->mutex); - return result; -} - - -void uwb_dev_rm(struct uwb_dev *uwb_dev) -{ - mutex_lock(&uwb_dev->mutex); - __uwb_dev_sys_rm(uwb_dev); - mutex_unlock(&uwb_dev->mutex); -} - - -static -int __uwb_dev_try_get(struct device *dev, void *__target_uwb_dev) -{ - struct uwb_dev *target_uwb_dev = __target_uwb_dev; - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - if (uwb_dev == target_uwb_dev) { - uwb_dev_get(uwb_dev); - return 1; - } else - return 0; -} - - -/** - * Given a UWB device descriptor, validate and refcount it - * - * @returns NULL if the device does not exist or is quiescing; the ptr to - * it otherwise. - */ -struct uwb_dev *uwb_dev_try_get(struct uwb_rc *rc, struct uwb_dev *uwb_dev) -{ - if (uwb_dev_for_each(rc, __uwb_dev_try_get, uwb_dev)) - return uwb_dev; - else - return NULL; -} -EXPORT_SYMBOL_GPL(uwb_dev_try_get); - - -/** - * Remove a device from the system [grunt for other functions] - */ -int __uwb_dev_offair(struct uwb_dev *uwb_dev, struct uwb_rc *rc) -{ - struct device *dev = &uwb_dev->dev; - char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE]; - - uwb_mac_addr_print(macbuf, sizeof(macbuf), &uwb_dev->mac_addr); - uwb_dev_addr_print(devbuf, sizeof(devbuf), &uwb_dev->dev_addr); - dev_info(dev, "uwb device (mac %s dev %s) disconnected from %s %s\n", - macbuf, devbuf, - uwb_dev->dev.bus->name, - rc ? dev_name(&(rc->uwb_dev.dev)) : ""); - uwb_dev_rm(uwb_dev); - list_del(&uwb_dev->bce->node); - uwb_bce_put(uwb_dev->bce); - uwb_dev_put(uwb_dev); /* for the creation in _onair() */ - - return 0; -} - - -/** - * A device went off the air, clean up after it! - * - * This is called by the UWB Daemon (through the beacon purge function - * uwb_bcn_cache_purge) when it is detected that a device has been in - * radio silence for a while. - * - * If this device is actually a local radio controller we don't need - * to go through the offair process, as it is not registered as that. - * - * NOTE: uwb_bcn_cache.mutex is held! - */ -void uwbd_dev_offair(struct uwb_beca_e *bce) -{ - struct uwb_dev *uwb_dev; - - uwb_dev = bce->uwb_dev; - if (uwb_dev) { - uwb_notify(uwb_dev->rc, uwb_dev, UWB_NOTIF_OFFAIR); - __uwb_dev_offair(uwb_dev, uwb_dev->rc); - } -} - - -/** - * A device went on the air, start it up! - * - * This is called by the UWB Daemon when it is detected that a device - * has popped up in the radio range of the radio controller. - * - * It will just create the freaking device, register the beacon and - * stuff and yatla, done. - * - * - * NOTE: uwb_beca.mutex is held, bce->mutex is held - */ -void uwbd_dev_onair(struct uwb_rc *rc, struct uwb_beca_e *bce) -{ - int result; - struct device *dev = &rc->uwb_dev.dev; - struct uwb_dev *uwb_dev; - char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE]; - - uwb_mac_addr_print(macbuf, sizeof(macbuf), bce->mac_addr); - uwb_dev_addr_print(devbuf, sizeof(devbuf), &bce->dev_addr); - uwb_dev = kzalloc(sizeof(struct uwb_dev), GFP_KERNEL); - if (uwb_dev == NULL) { - dev_err(dev, "new device %s: Cannot allocate memory\n", - macbuf); - return; - } - uwb_dev_init(uwb_dev); /* This sets refcnt to one, we own it */ - uwb_dev->dev.bus = &uwb_bus_type; - uwb_dev->mac_addr = *bce->mac_addr; - uwb_dev->dev_addr = bce->dev_addr; - dev_set_name(&uwb_dev->dev, "%s", macbuf); - - /* plug the beacon cache */ - bce->uwb_dev = uwb_dev; - uwb_dev->bce = bce; - uwb_bce_get(bce); /* released in uwb_dev_sys_release() */ - - result = uwb_dev_add(uwb_dev, &rc->uwb_dev.dev, rc); - if (result < 0) { - dev_err(dev, "new device %s: cannot instantiate device\n", - macbuf); - goto error_dev_add; - } - - dev_info(dev, "uwb device (mac %s dev %s) connected to %s %s\n", - macbuf, devbuf, uwb_dev->dev.bus->name, - dev_name(&(rc->uwb_dev.dev))); - uwb_notify(rc, uwb_dev, UWB_NOTIF_ONAIR); - return; - -error_dev_add: - bce->uwb_dev = NULL; - uwb_bce_put(bce); - kfree(uwb_dev); - return; -} - -/** - * Iterate over the list of UWB devices, calling a @function on each - * - * See docs for bus_for_each().... - * - * @rc: radio controller for the devices. - * @function: function to call. - * @priv: data to pass to @function. - * @returns: 0 if no invocation of function() returned a value - * different to zero. That value otherwise. - */ -int uwb_dev_for_each(struct uwb_rc *rc, uwb_dev_for_each_f function, void *priv) -{ - return device_for_each_child(&rc->uwb_dev.dev, priv, function); -} -EXPORT_SYMBOL_GPL(uwb_dev_for_each); diff --git a/drivers/staging/uwb/lc-rc.c b/drivers/staging/uwb/lc-rc.c deleted file mode 100644 index ee31b221cdc2..000000000000 --- a/drivers/staging/uwb/lc-rc.c +++ /dev/null @@ -1,569 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Life cycle of radio controllers - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - * - * A UWB radio controller is also a UWB device, so it embeds one... - * - * List of RCs comes from the 'struct class uwb_rc_class'. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "uwb-internal.h" - -static int uwb_rc_index_match(struct device *dev, const void *data) -{ - const int *index = data; - struct uwb_rc *rc = dev_get_drvdata(dev); - - if (rc->index == *index) - return 1; - return 0; -} - -static struct uwb_rc *uwb_rc_find_by_index(int index) -{ - struct device *dev; - struct uwb_rc *rc = NULL; - - dev = class_find_device(&uwb_rc_class, NULL, &index, uwb_rc_index_match); - if (dev) { - rc = dev_get_drvdata(dev); - put_device(dev); - } - - return rc; -} - -static int uwb_rc_new_index(void) -{ - int index = 0; - - for (;;) { - if (!uwb_rc_find_by_index(index)) - return index; - if (++index < 0) - index = 0; - } -} - -/** - * Release the backing device of a uwb_rc that has been dynamically allocated. - */ -static void uwb_rc_sys_release(struct device *dev) -{ - struct uwb_dev *uwb_dev = container_of(dev, struct uwb_dev, dev); - struct uwb_rc *rc = container_of(uwb_dev, struct uwb_rc, uwb_dev); - - uwb_rc_ie_release(rc); - kfree(rc); -} - - -void uwb_rc_init(struct uwb_rc *rc) -{ - struct uwb_dev *uwb_dev = &rc->uwb_dev; - - uwb_dev_init(uwb_dev); - rc->uwb_dev.dev.class = &uwb_rc_class; - rc->uwb_dev.dev.release = uwb_rc_sys_release; - uwb_rc_neh_create(rc); - rc->beaconing = -1; - rc->scan_type = UWB_SCAN_DISABLED; - INIT_LIST_HEAD(&rc->notifs_chain.list); - mutex_init(&rc->notifs_chain.mutex); - INIT_LIST_HEAD(&rc->uwb_beca.list); - mutex_init(&rc->uwb_beca.mutex); - uwb_drp_avail_init(rc); - uwb_rc_ie_init(rc); - uwb_rsv_init(rc); - uwb_rc_pal_init(rc); -} -EXPORT_SYMBOL_GPL(uwb_rc_init); - - -struct uwb_rc *uwb_rc_alloc(void) -{ - struct uwb_rc *rc; - rc = kzalloc(sizeof(*rc), GFP_KERNEL); - if (rc == NULL) - return NULL; - uwb_rc_init(rc); - return rc; -} -EXPORT_SYMBOL_GPL(uwb_rc_alloc); - -/* - * Show the ASIE that is broadcast in the UWB beacon by this uwb_rc device. - */ -static ssize_t ASIE_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_rc *rc = uwb_dev->rc; - struct uwb_ie_hdr *ie; - void *ptr; - size_t len; - int result = 0; - - /* init empty buffer. */ - result = scnprintf(buf, PAGE_SIZE, "\n"); - mutex_lock(&rc->ies_mutex); - /* walk IEData looking for an ASIE. */ - ptr = rc->ies->IEData; - len = le16_to_cpu(rc->ies->wIELength); - for (;;) { - ie = uwb_ie_next(&ptr, &len); - if (!ie) - break; - if (ie->element_id == UWB_APP_SPEC_IE) { - result = uwb_ie_dump_hex(ie, - ie->length + sizeof(struct uwb_ie_hdr), - buf, PAGE_SIZE); - break; - } - } - mutex_unlock(&rc->ies_mutex); - - return result; -} - -/* - * Update the ASIE that is broadcast in the UWB beacon by this uwb_rc device. - */ -static ssize_t ASIE_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_rc *rc = uwb_dev->rc; - char ie_buf[255]; - int result, ie_len = 0; - const char *cur_ptr = buf; - struct uwb_ie_hdr *ie; - - /* empty string means clear the ASIE. */ - if (strlen(buf) <= 1) { - uwb_rc_ie_rm(rc, UWB_APP_SPEC_IE); - return size; - } - - /* if non-empty string, convert string of hex chars to binary. */ - while (ie_len < sizeof(ie_buf)) { - int char_count; - - if (sscanf(cur_ptr, " %02hhX %n", - &(ie_buf[ie_len]), &char_count) > 0) { - ++ie_len; - /* skip chars read from cur_ptr. */ - cur_ptr += char_count; - } else { - break; - } - } - - /* validate IE length and type. */ - if (ie_len < sizeof(struct uwb_ie_hdr)) { - dev_err(dev, "%s: Invalid ASIE size %d.\n", __func__, ie_len); - return -EINVAL; - } - - ie = (struct uwb_ie_hdr *)ie_buf; - if (ie->element_id != UWB_APP_SPEC_IE) { - dev_err(dev, "%s: Invalid IE element type size = 0x%02X.\n", - __func__, ie->element_id); - return -EINVAL; - } - - /* bounds check length field from user. */ - if (ie->length > (ie_len - sizeof(struct uwb_ie_hdr))) - ie->length = ie_len - sizeof(struct uwb_ie_hdr); - - /* - * Valid ASIE received. Remove current ASIE then add the new one using - * uwb_rc_ie_add. - */ - uwb_rc_ie_rm(rc, UWB_APP_SPEC_IE); - - result = uwb_rc_ie_add(rc, ie, ie->length + sizeof(struct uwb_ie_hdr)); - - return result >= 0 ? size : result; -} -static DEVICE_ATTR_RW(ASIE); - -static struct attribute *rc_attrs[] = { - &dev_attr_mac_address.attr, - &dev_attr_scan.attr, - &dev_attr_beacon.attr, - &dev_attr_ASIE.attr, - NULL, -}; - -static const struct attribute_group rc_attr_group = { - .attrs = rc_attrs, -}; - -/* - * Registration of sysfs specific stuff - */ -static int uwb_rc_sys_add(struct uwb_rc *rc) -{ - return sysfs_create_group(&rc->uwb_dev.dev.kobj, &rc_attr_group); -} - - -static void __uwb_rc_sys_rm(struct uwb_rc *rc) -{ - sysfs_remove_group(&rc->uwb_dev.dev.kobj, &rc_attr_group); -} - -/** - * uwb_rc_mac_addr_setup - get an RC's EUI-48 address or set it - * @rc: the radio controller. - * - * If the EUI-48 address is 00:00:00:00:00:00 or FF:FF:FF:FF:FF:FF - * then a random locally administered EUI-48 is generated and set on - * the device. The probability of address collisions is sufficiently - * unlikely (1/2^40 = 9.1e-13) that they're not checked for. - */ -static -int uwb_rc_mac_addr_setup(struct uwb_rc *rc) -{ - int result; - struct device *dev = &rc->uwb_dev.dev; - struct uwb_dev *uwb_dev = &rc->uwb_dev; - char devname[UWB_ADDR_STRSIZE]; - struct uwb_mac_addr addr; - - result = uwb_rc_mac_addr_get(rc, &addr); - if (result < 0) { - dev_err(dev, "cannot retrieve UWB EUI-48 address: %d\n", result); - return result; - } - - if (uwb_mac_addr_unset(&addr) || uwb_mac_addr_bcast(&addr)) { - addr.data[0] = 0x02; /* locally administered and unicast */ - get_random_bytes(&addr.data[1], sizeof(addr.data)-1); - - result = uwb_rc_mac_addr_set(rc, &addr); - if (result < 0) { - uwb_mac_addr_print(devname, sizeof(devname), &addr); - dev_err(dev, "cannot set EUI-48 address %s: %d\n", - devname, result); - return result; - } - } - uwb_dev->mac_addr = addr; - return 0; -} - - - -static int uwb_rc_setup(struct uwb_rc *rc) -{ - int result; - struct device *dev = &rc->uwb_dev.dev; - - result = uwb_radio_setup(rc); - if (result < 0) { - dev_err(dev, "cannot setup UWB radio: %d\n", result); - goto error; - } - result = uwb_rc_mac_addr_setup(rc); - if (result < 0) { - dev_err(dev, "cannot setup UWB MAC address: %d\n", result); - goto error; - } - result = uwb_rc_dev_addr_assign(rc); - if (result < 0) { - dev_err(dev, "cannot assign UWB DevAddr: %d\n", result); - goto error; - } - result = uwb_rc_ie_setup(rc); - if (result < 0) { - dev_err(dev, "cannot setup IE subsystem: %d\n", result); - goto error_ie_setup; - } - result = uwb_rsv_setup(rc); - if (result < 0) { - dev_err(dev, "cannot setup reservation subsystem: %d\n", result); - goto error_rsv_setup; - } - uwb_dbg_add_rc(rc); - return 0; - -error_rsv_setup: - uwb_rc_ie_release(rc); -error_ie_setup: -error: - return result; -} - - -/** - * Register a new UWB radio controller - * - * Did you call uwb_rc_init() on your rc? - * - * We assume that this is being called with a > 0 refcount on - * it [through ops->{get|put}_device(). We'll take our own, though. - * - * @parent_dev is our real device, the one that provides the actual UWB device - */ -int uwb_rc_add(struct uwb_rc *rc, struct device *parent_dev, void *priv) -{ - int result; - struct device *dev; - char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE]; - - rc->index = uwb_rc_new_index(); - - dev = &rc->uwb_dev.dev; - dev_set_name(dev, "uwb%d", rc->index); - - rc->priv = priv; - - init_waitqueue_head(&rc->uwbd.wq); - INIT_LIST_HEAD(&rc->uwbd.event_list); - spin_lock_init(&rc->uwbd.event_list_lock); - - uwbd_start(rc); - - result = rc->start(rc); - if (result < 0) - goto error_rc_start; - - result = uwb_rc_setup(rc); - if (result < 0) { - dev_err(dev, "cannot setup UWB radio controller: %d\n", result); - goto error_rc_setup; - } - - result = uwb_dev_add(&rc->uwb_dev, parent_dev, rc); - if (result < 0 && result != -EADDRNOTAVAIL) - goto error_dev_add; - - result = uwb_rc_sys_add(rc); - if (result < 0) { - dev_err(parent_dev, "cannot register UWB radio controller " - "dev attributes: %d\n", result); - goto error_sys_add; - } - - uwb_mac_addr_print(macbuf, sizeof(macbuf), &rc->uwb_dev.mac_addr); - uwb_dev_addr_print(devbuf, sizeof(devbuf), &rc->uwb_dev.dev_addr); - dev_info(dev, - "new uwb radio controller (mac %s dev %s) on %s %s\n", - macbuf, devbuf, parent_dev->bus->name, dev_name(parent_dev)); - rc->ready = 1; - return 0; - -error_sys_add: - uwb_dev_rm(&rc->uwb_dev); -error_dev_add: -error_rc_setup: - rc->stop(rc); -error_rc_start: - uwbd_stop(rc); - return result; -} -EXPORT_SYMBOL_GPL(uwb_rc_add); - - -static int uwb_dev_offair_helper(struct device *dev, void *priv) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - - return __uwb_dev_offair(uwb_dev, uwb_dev->rc); -} - -/* - * Remove a Radio Controller; stop beaconing/scanning, disconnect all children - */ -void uwb_rc_rm(struct uwb_rc *rc) -{ - rc->ready = 0; - - uwb_dbg_del_rc(rc); - uwb_rsv_remove_all(rc); - uwb_radio_shutdown(rc); - - rc->stop(rc); - - uwbd_stop(rc); - uwb_rc_neh_destroy(rc); - - uwb_dev_lock(&rc->uwb_dev); - rc->priv = NULL; - rc->cmd = NULL; - uwb_dev_unlock(&rc->uwb_dev); - mutex_lock(&rc->uwb_beca.mutex); - uwb_dev_for_each(rc, uwb_dev_offair_helper, NULL); - __uwb_rc_sys_rm(rc); - mutex_unlock(&rc->uwb_beca.mutex); - uwb_rsv_cleanup(rc); - uwb_beca_release(rc); - uwb_dev_rm(&rc->uwb_dev); -} -EXPORT_SYMBOL_GPL(uwb_rc_rm); - -static int find_rc_try_get(struct device *dev, const void *data) -{ - const struct uwb_rc *target_rc = data; - struct uwb_rc *rc = dev_get_drvdata(dev); - - if (rc == NULL) { - WARN_ON(1); - return 0; - } - if (rc == target_rc) { - if (rc->ready == 0) - return 0; - else - return 1; - } - return 0; -} - -/** - * Given a radio controller descriptor, validate and refcount it - * - * @returns NULL if the rc does not exist or is quiescing; the ptr to - * it otherwise. - */ -struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc) -{ - struct device *dev; - struct uwb_rc *rc = NULL; - - dev = class_find_device(&uwb_rc_class, NULL, target_rc, - find_rc_try_get); - if (dev) { - rc = dev_get_drvdata(dev); - __uwb_rc_get(rc); - put_device(dev); - } - - return rc; -} -EXPORT_SYMBOL_GPL(__uwb_rc_try_get); - -/* - * RC get for external refcount acquirers... - * - * Increments the refcount of the device and it's backend modules - */ -static inline struct uwb_rc *uwb_rc_get(struct uwb_rc *rc) -{ - if (rc->ready == 0) - return NULL; - uwb_dev_get(&rc->uwb_dev); - return rc; -} - -static int find_rc_grandpa(struct device *dev, const void *data) -{ - const struct device *grandpa_dev = data; - struct uwb_rc *rc = dev_get_drvdata(dev); - - if (rc->uwb_dev.dev.parent->parent == grandpa_dev) { - rc = uwb_rc_get(rc); - return 1; - } - return 0; -} - -/** - * Locate and refcount a radio controller given a common grand-parent - * - * @grandpa_dev Pointer to the 'grandparent' device structure. - * @returns NULL If the rc does not exist or is quiescing; the ptr to - * it otherwise, properly referenced. - * - * The Radio Control interface (or the UWB Radio Controller) is always - * an interface of a device. The parent is the interface, the - * grandparent is the device that encapsulates the interface. - * - * There is no need to lock around as the "grandpa" would be - * refcounted by the target, and to remove the referemes, the - * uwb_rc_class->sem would have to be taken--we hold it, ergo we - * should be safe. - */ -struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev) -{ - struct device *dev; - struct uwb_rc *rc = NULL; - - dev = class_find_device(&uwb_rc_class, NULL, grandpa_dev, - find_rc_grandpa); - if (dev) { - rc = dev_get_drvdata(dev); - put_device(dev); - } - - return rc; -} -EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa); - -/** - * Find a radio controller by device address - * - * @returns the pointer to the radio controller, properly referenced - */ -static int find_rc_dev(struct device *dev, const void *data) -{ - const struct uwb_dev_addr *addr = data; - struct uwb_rc *rc = dev_get_drvdata(dev); - - if (rc == NULL) { - WARN_ON(1); - return 0; - } - if (!uwb_dev_addr_cmp(&rc->uwb_dev.dev_addr, addr)) { - rc = uwb_rc_get(rc); - return 1; - } - return 0; -} - -struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr) -{ - struct device *dev; - struct uwb_rc *rc = NULL; - - dev = class_find_device(&uwb_rc_class, NULL, addr, find_rc_dev); - if (dev) { - rc = dev_get_drvdata(dev); - put_device(dev); - } - - return rc; -} -EXPORT_SYMBOL_GPL(uwb_rc_get_by_dev); - -/** - * Drop a reference on a radio controller - * - * This is the version that should be done by entities external to the - * UWB Radio Control stack (ie: clients of the API). - */ -void uwb_rc_put(struct uwb_rc *rc) -{ - __uwb_rc_put(rc); -} -EXPORT_SYMBOL_GPL(uwb_rc_put); diff --git a/drivers/staging/uwb/neh.c b/drivers/staging/uwb/neh.c deleted file mode 100644 index 1695584be412..000000000000 --- a/drivers/staging/uwb/neh.c +++ /dev/null @@ -1,606 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * WUSB Wire Adapter: Radio Control Interface (WUSB[8]) - * Notification and Event Handling - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * The RC interface of the Host Wire Adapter (USB dongle) or WHCI PCI - * card delivers a stream of notifications and events to the - * notification end event endpoint or area. This code takes care of - * getting a buffer with that data, breaking it up in separate - * notifications and events and then deliver those. - * - * Events are answers to commands and they carry a context ID that - * associates them to the command. Notifications are that, - * notifications, they come out of the blue and have a context ID of - * zero. Think of the context ID kind of like a handler. The - * uwb_rc_neh_* code deals with managing context IDs. - * - * This is why you require a handle to operate on a UWB host. When you - * open a handle a context ID is assigned to you. - * - * So, as it is done is: - * - * 1. Add an event handler [uwb_rc_neh_add()] (assigns a ctx id) - * 2. Issue command [rc->cmd(rc, ...)] - * 3. Arm the timeout timer [uwb_rc_neh_arm()] - * 4, Release the reference to the neh [uwb_rc_neh_put()] - * 5. Wait for the callback - * 6. Command result (RCEB) is passed to the callback - * - * If (2) fails, you should remove the handle [uwb_rc_neh_rm()] - * instead of arming the timer. - * - * Handles are for using in *serialized* code, single thread. - * - * When the notification/event comes, the IRQ handler/endpoint - * callback passes the data read to uwb_rc_neh_grok() which will break - * it up in a discrete series of events, look up who is listening for - * them and execute the pertinent callbacks. - * - * If the reader detects an error while reading the data stream, call - * uwb_rc_neh_error(). - * - * CONSTRAINTS/ASSUMPTIONS: - * - * - Most notifications/events are small (less thank .5k), copying - * around is ok. - * - * - Notifications/events are ALWAYS smaller than PAGE_SIZE - * - * - Notifications/events always come in a single piece (ie: a buffer - * will always contain entire notifications/events). - * - * - we cannot know in advance how long each event is (because they - * lack a length field in their header--smart move by the standards - * body, btw). So we need a facility to get the event size given the - * header. This is what the EST code does (notif/Event Size - * Tables), check nest.c--as well, you can associate the size to - * the handle [w/ neh->extra_size()]. - * - * - Most notifications/events are fixed size; only a few are variable - * size (NEST takes care of that). - * - * - Listeners of events expect them, so they usually provide a - * buffer, as they know the size. Listeners to notifications don't, - * so we allocate their buffers dynamically. - */ -#include -#include -#include -#include -#include - -#include "uwb-internal.h" - -/* - * UWB Radio Controller Notification/Event Handle - * - * Represents an entity waiting for an event coming from the UWB Radio - * Controller with a given context id (context) and type (evt_type and - * evt). On reception of the notification/event, the callback (cb) is - * called with the event. - * - * If the timer expires before the event is received, the callback is - * called with -ETIMEDOUT as the event size. - */ -struct uwb_rc_neh { - struct kref kref; - - struct uwb_rc *rc; - u8 evt_type; - __le16 evt; - u8 context; - u8 completed; - uwb_rc_cmd_cb_f cb; - void *arg; - - struct timer_list timer; - struct list_head list_node; -}; - -static void uwb_rc_neh_timer(struct timer_list *t); - -static void uwb_rc_neh_release(struct kref *kref) -{ - struct uwb_rc_neh *neh = container_of(kref, struct uwb_rc_neh, kref); - - kfree(neh); -} - -static void uwb_rc_neh_get(struct uwb_rc_neh *neh) -{ - kref_get(&neh->kref); -} - -/** - * uwb_rc_neh_put - release reference to a neh - * @neh: the neh - */ -void uwb_rc_neh_put(struct uwb_rc_neh *neh) -{ - kref_put(&neh->kref, uwb_rc_neh_release); -} - - -/** - * Assigns @neh a context id from @rc's pool - * - * @rc: UWB Radio Controller descriptor; @rc->neh_lock taken - * @neh: Notification/Event Handle - * @returns 0 if context id was assigned ok; < 0 errno on error (if - * all the context IDs are taken). - * - * (assumes @wa is locked). - * - * NOTE: WUSB spec reserves context ids 0x00 for notifications and - * 0xff is invalid, so they must not be used. Initialization - * fills up those two in the bitmap so they are not allocated. - * - * We spread the allocation around to reduce the possibility of two - * consecutive opened @neh's getting the same context ID assigned (to - * avoid surprises with late events that timed out long time ago). So - * first we search from where @rc->ctx_roll is, if not found, we - * search from zero. - */ -static -int __uwb_rc_ctx_get(struct uwb_rc *rc, struct uwb_rc_neh *neh) -{ - int result; - result = find_next_zero_bit(rc->ctx_bm, UWB_RC_CTX_MAX, - rc->ctx_roll++); - if (result < UWB_RC_CTX_MAX) - goto found; - result = find_first_zero_bit(rc->ctx_bm, UWB_RC_CTX_MAX); - if (result < UWB_RC_CTX_MAX) - goto found; - return -ENFILE; -found: - set_bit(result, rc->ctx_bm); - neh->context = result; - return 0; -} - - -/** Releases @neh's context ID back to @rc (@rc->neh_lock is locked). */ -static -void __uwb_rc_ctx_put(struct uwb_rc *rc, struct uwb_rc_neh *neh) -{ - struct device *dev = &rc->uwb_dev.dev; - if (neh->context == 0) - return; - if (test_bit(neh->context, rc->ctx_bm) == 0) { - dev_err(dev, "context %u not set in bitmap\n", - neh->context); - WARN_ON(1); - } - clear_bit(neh->context, rc->ctx_bm); - neh->context = 0; -} - -/** - * uwb_rc_neh_add - add a neh for a radio controller command - * @rc: the radio controller - * @cmd: the radio controller command - * @expected_type: the type of the expected response event - * @expected_event: the expected event ID - * @cb: callback for when the event is received - * @arg: argument for the callback - * - * Creates a neh and adds it to the list of those waiting for an - * event. A context ID will be assigned to the command. - */ -struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd, - u8 expected_type, u16 expected_event, - uwb_rc_cmd_cb_f cb, void *arg) -{ - int result; - unsigned long flags; - struct device *dev = &rc->uwb_dev.dev; - struct uwb_rc_neh *neh; - - neh = kzalloc(sizeof(*neh), GFP_KERNEL); - if (neh == NULL) { - result = -ENOMEM; - goto error_kzalloc; - } - - kref_init(&neh->kref); - INIT_LIST_HEAD(&neh->list_node); - timer_setup(&neh->timer, uwb_rc_neh_timer, 0); - - neh->rc = rc; - neh->evt_type = expected_type; - neh->evt = cpu_to_le16(expected_event); - neh->cb = cb; - neh->arg = arg; - - spin_lock_irqsave(&rc->neh_lock, flags); - result = __uwb_rc_ctx_get(rc, neh); - if (result >= 0) { - cmd->bCommandContext = neh->context; - list_add_tail(&neh->list_node, &rc->neh_list); - uwb_rc_neh_get(neh); - } - spin_unlock_irqrestore(&rc->neh_lock, flags); - if (result < 0) - goto error_ctx_get; - - return neh; - -error_ctx_get: - kfree(neh); -error_kzalloc: - dev_err(dev, "cannot open handle to radio controller: %d\n", result); - return ERR_PTR(result); -} - -static void __uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh) -{ - __uwb_rc_ctx_put(rc, neh); - list_del(&neh->list_node); -} - -/** - * uwb_rc_neh_rm - remove a neh. - * @rc: the radio controller - * @neh: the neh to remove - * - * Remove an active neh immediately instead of waiting for the event - * (or a time out). - */ -void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh) -{ - unsigned long flags; - - spin_lock_irqsave(&rc->neh_lock, flags); - __uwb_rc_neh_rm(rc, neh); - spin_unlock_irqrestore(&rc->neh_lock, flags); - - del_timer_sync(&neh->timer); - uwb_rc_neh_put(neh); -} - -/** - * uwb_rc_neh_arm - arm an event handler timeout timer - * - * @rc: UWB Radio Controller - * @neh: Notification/event handler for @rc - * - * The timer is only armed if the neh is active. - */ -void uwb_rc_neh_arm(struct uwb_rc *rc, struct uwb_rc_neh *neh) -{ - unsigned long flags; - - spin_lock_irqsave(&rc->neh_lock, flags); - if (neh->context) - mod_timer(&neh->timer, - jiffies + msecs_to_jiffies(UWB_RC_CMD_TIMEOUT_MS)); - spin_unlock_irqrestore(&rc->neh_lock, flags); -} - -static void uwb_rc_neh_cb(struct uwb_rc_neh *neh, struct uwb_rceb *rceb, size_t size) -{ - (*neh->cb)(neh->rc, neh->arg, rceb, size); - uwb_rc_neh_put(neh); -} - -static bool uwb_rc_neh_match(struct uwb_rc_neh *neh, const struct uwb_rceb *rceb) -{ - return neh->evt_type == rceb->bEventType - && neh->evt == rceb->wEvent - && neh->context == rceb->bEventContext; -} - -/** - * Find the handle waiting for a RC Radio Control Event - * - * @rc: UWB Radio Controller - * @rceb: Pointer to the RCEB buffer - * @event_size: Pointer to the size of the RCEB buffer. Might be - * adjusted to take into account the @neh->extra_size - * settings. - * - * If the listener has no buffer (NULL buffer), one is allocated for - * the right size (the amount of data received). @neh->ptr will point - * to the event payload, which always starts with a 'struct - * uwb_rceb'. kfree() it when done. - */ -static -struct uwb_rc_neh *uwb_rc_neh_lookup(struct uwb_rc *rc, - const struct uwb_rceb *rceb) -{ - struct uwb_rc_neh *neh = NULL, *h; - unsigned long flags; - - spin_lock_irqsave(&rc->neh_lock, flags); - - list_for_each_entry(h, &rc->neh_list, list_node) { - if (uwb_rc_neh_match(h, rceb)) { - neh = h; - break; - } - } - - if (neh) - __uwb_rc_neh_rm(rc, neh); - - spin_unlock_irqrestore(&rc->neh_lock, flags); - - return neh; -} - - -/* - * Process notifications coming from the radio control interface - * - * @rc: UWB Radio Control Interface descriptor - * @neh: Notification/Event Handler @neh->ptr points to - * @uwb_evt->buffer. - * - * This function is called by the event/notif handling subsystem when - * notifications arrive (hwarc_probe() arms a notification/event handle - * that calls back this function for every received notification; this - * function then will rearm itself). - * - * Notification data buffers are dynamically allocated by the NEH - * handling code in neh.c [uwb_rc_neh_lookup()]. What is actually - * allocated is space to contain the notification data. - * - * Buffers are prefixed with a Radio Control Event Block (RCEB) as - * defined by the WUSB Wired-Adapter Radio Control interface. We - * just use it for the notification code. - * - * On each case statement we just transcode endianess of the different - * fields. We declare a pointer to a RCI definition of an event, and - * then to a UWB definition of the same event (which are the same, - * remember). Event if we use different pointers - */ -static -void uwb_rc_notif(struct uwb_rc *rc, struct uwb_rceb *rceb, ssize_t size) -{ - struct device *dev = &rc->uwb_dev.dev; - struct uwb_event *uwb_evt; - - if (size == -ESHUTDOWN) - return; - if (size < 0) { - dev_err(dev, "ignoring event with error code %zu\n", - size); - return; - } - - uwb_evt = kzalloc(sizeof(*uwb_evt), GFP_ATOMIC); - if (unlikely(uwb_evt == NULL)) { - dev_err(dev, "no memory to queue event 0x%02x/%04x/%02x\n", - rceb->bEventType, le16_to_cpu(rceb->wEvent), - rceb->bEventContext); - return; - } - uwb_evt->rc = __uwb_rc_get(rc); /* will be put by uwbd's uwbd_event_handle() */ - uwb_evt->ts_jiffies = jiffies; - uwb_evt->type = UWB_EVT_TYPE_NOTIF; - uwb_evt->notif.size = size; - uwb_evt->notif.rceb = rceb; - - uwbd_event_queue(uwb_evt); -} - -static void uwb_rc_neh_grok_event(struct uwb_rc *rc, struct uwb_rceb *rceb, size_t size) -{ - struct device *dev = &rc->uwb_dev.dev; - struct uwb_rc_neh *neh; - struct uwb_rceb *notif; - unsigned long flags; - - if (rceb->bEventContext == 0) { - notif = kmalloc(size, GFP_ATOMIC); - if (notif) { - memcpy(notif, rceb, size); - uwb_rc_notif(rc, notif, size); - } else - dev_err(dev, "event 0x%02x/%04x/%02x (%zu bytes): no memory\n", - rceb->bEventType, le16_to_cpu(rceb->wEvent), - rceb->bEventContext, size); - } else { - neh = uwb_rc_neh_lookup(rc, rceb); - if (neh) { - spin_lock_irqsave(&rc->neh_lock, flags); - /* to guard against a timeout */ - neh->completed = 1; - del_timer(&neh->timer); - spin_unlock_irqrestore(&rc->neh_lock, flags); - uwb_rc_neh_cb(neh, rceb, size); - } else - dev_warn(dev, "event 0x%02x/%04x/%02x (%zu bytes): nobody cared\n", - rceb->bEventType, le16_to_cpu(rceb->wEvent), - rceb->bEventContext, size); - } -} - -/** - * Given a buffer with one or more UWB RC events/notifications, break - * them up and dispatch them. - * - * @rc: UWB Radio Controller - * @buf: Buffer with the stream of notifications/events - * @buf_size: Amount of data in the buffer - * - * Note each notification/event starts always with a 'struct - * uwb_rceb', so the minimum size if 4 bytes. - * - * The device may pass us events formatted differently than expected. - * These are first filtered, potentially creating a new event in a new - * memory location. If a new event is created by the filter it is also - * freed here. - * - * For each notif/event, tries to guess the size looking at the EST - * tables, then looks for a neh that is waiting for that event and if - * found, copies the payload to the neh's buffer and calls it back. If - * not, the data is ignored. - * - * Note that if we can't find a size description in the EST tables, we - * still might find a size in the 'neh' handle in uwb_rc_neh_lookup(). - * - * Assumptions: - * - * @rc->neh_lock is NOT taken - * - * We keep track of various sizes here: - * size: contains the size of the buffer that is processed for the - * incoming event. this buffer may contain events that are not - * formatted as WHCI. - * real_size: the actual space taken by this event in the buffer. - * We need to keep track of the real size of an event to be able to - * advance the buffer correctly. - * event_size: the size of the event as expected by the core layer - * [OR] the size of the event after filtering. if the filtering - * created a new event in a new memory location then this is - * effectively the size of a new event buffer - */ -void uwb_rc_neh_grok(struct uwb_rc *rc, void *buf, size_t buf_size) -{ - struct device *dev = &rc->uwb_dev.dev; - void *itr; - struct uwb_rceb *rceb; - size_t size, real_size, event_size; - int needtofree; - - itr = buf; - size = buf_size; - while (size > 0) { - if (size < sizeof(*rceb)) { - dev_err(dev, "not enough data in event buffer to " - "process incoming events (%zu left, minimum is " - "%zu)\n", size, sizeof(*rceb)); - break; - } - - rceb = itr; - if (rc->filter_event) { - needtofree = rc->filter_event(rc, &rceb, size, - &real_size, &event_size); - if (needtofree < 0 && needtofree != -ENOANO) { - dev_err(dev, "BUG: Unable to filter event " - "(0x%02x/%04x/%02x) from " - "device. \n", rceb->bEventType, - le16_to_cpu(rceb->wEvent), - rceb->bEventContext); - break; - } - } else - needtofree = -ENOANO; - /* do real processing if there was no filtering or the - * filtering didn't act */ - if (needtofree == -ENOANO) { - ssize_t ret = uwb_est_find_size(rc, rceb, size); - if (ret < 0) - break; - if (ret > size) { - dev_err(dev, "BUG: hw sent incomplete event " - "0x%02x/%04x/%02x (%zd bytes), only got " - "%zu bytes. We don't handle that.\n", - rceb->bEventType, le16_to_cpu(rceb->wEvent), - rceb->bEventContext, ret, size); - break; - } - real_size = event_size = ret; - } - uwb_rc_neh_grok_event(rc, rceb, event_size); - - if (needtofree == 1) - kfree(rceb); - - itr += real_size; - size -= real_size; - } -} -EXPORT_SYMBOL_GPL(uwb_rc_neh_grok); - - -/** - * The entity that reads from the device notification/event channel has - * detected an error. - * - * @rc: UWB Radio Controller - * @error: Errno error code - * - */ -void uwb_rc_neh_error(struct uwb_rc *rc, int error) -{ - struct uwb_rc_neh *neh; - unsigned long flags; - - for (;;) { - spin_lock_irqsave(&rc->neh_lock, flags); - if (list_empty(&rc->neh_list)) { - spin_unlock_irqrestore(&rc->neh_lock, flags); - break; - } - neh = list_first_entry(&rc->neh_list, struct uwb_rc_neh, list_node); - __uwb_rc_neh_rm(rc, neh); - spin_unlock_irqrestore(&rc->neh_lock, flags); - - del_timer_sync(&neh->timer); - uwb_rc_neh_cb(neh, NULL, error); - } -} -EXPORT_SYMBOL_GPL(uwb_rc_neh_error); - - -static void uwb_rc_neh_timer(struct timer_list *t) -{ - struct uwb_rc_neh *neh = from_timer(neh, t, timer); - struct uwb_rc *rc = neh->rc; - unsigned long flags; - - spin_lock_irqsave(&rc->neh_lock, flags); - if (neh->completed) { - spin_unlock_irqrestore(&rc->neh_lock, flags); - return; - } - if (neh->context) - __uwb_rc_neh_rm(rc, neh); - else - neh = NULL; - spin_unlock_irqrestore(&rc->neh_lock, flags); - - if (neh) - uwb_rc_neh_cb(neh, NULL, -ETIMEDOUT); -} - -/** Initializes the @rc's neh subsystem - */ -void uwb_rc_neh_create(struct uwb_rc *rc) -{ - spin_lock_init(&rc->neh_lock); - INIT_LIST_HEAD(&rc->neh_list); - set_bit(0, rc->ctx_bm); /* 0 is reserved (see [WUSB] table 8-65) */ - set_bit(0xff, rc->ctx_bm); /* and 0xff is invalid */ - rc->ctx_roll = 1; -} - - -/** Release's the @rc's neh subsystem */ -void uwb_rc_neh_destroy(struct uwb_rc *rc) -{ - unsigned long flags; - struct uwb_rc_neh *neh; - - for (;;) { - spin_lock_irqsave(&rc->neh_lock, flags); - if (list_empty(&rc->neh_list)) { - spin_unlock_irqrestore(&rc->neh_lock, flags); - break; - } - neh = list_first_entry(&rc->neh_list, struct uwb_rc_neh, list_node); - __uwb_rc_neh_rm(rc, neh); - spin_unlock_irqrestore(&rc->neh_lock, flags); - - del_timer_sync(&neh->timer); - uwb_rc_neh_put(neh); - } -} diff --git a/drivers/staging/uwb/pal.c b/drivers/staging/uwb/pal.c deleted file mode 100644 index a541e646a603..000000000000 --- a/drivers/staging/uwb/pal.c +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * UWB PAL support. - * - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include - -#include "uwb.h" -#include "uwb-internal.h" - -/** - * uwb_pal_init - initialize a UWB PAL - * @pal: the PAL to initialize - */ -void uwb_pal_init(struct uwb_pal *pal) -{ - INIT_LIST_HEAD(&pal->node); -} -EXPORT_SYMBOL_GPL(uwb_pal_init); - -/** - * uwb_pal_register - register a UWB PAL - * @pal: the PAL - * - * The PAL must be initialized with uwb_pal_init(). - */ -int uwb_pal_register(struct uwb_pal *pal) -{ - struct uwb_rc *rc = pal->rc; - int ret; - - if (pal->device) { - /* create a link to the uwb_rc in the PAL device's directory. */ - ret = sysfs_create_link(&pal->device->kobj, - &rc->uwb_dev.dev.kobj, "uwb_rc"); - if (ret < 0) - return ret; - /* create a link to the PAL in the UWB device's directory. */ - ret = sysfs_create_link(&rc->uwb_dev.dev.kobj, - &pal->device->kobj, pal->name); - if (ret < 0) { - sysfs_remove_link(&pal->device->kobj, "uwb_rc"); - return ret; - } - } - - pal->debugfs_dir = uwb_dbg_create_pal_dir(pal); - - mutex_lock(&rc->uwb_dev.mutex); - list_add(&pal->node, &rc->pals); - mutex_unlock(&rc->uwb_dev.mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(uwb_pal_register); - -static int find_rc(struct device *dev, const void *data) -{ - const struct uwb_rc *target_rc = data; - struct uwb_rc *rc = dev_get_drvdata(dev); - - if (rc == NULL) { - WARN_ON(1); - return 0; - } - if (rc == target_rc) { - if (rc->ready == 0) - return 0; - else - return 1; - } - return 0; -} - -/** - * Given a radio controller descriptor see if it is registered. - * - * @returns false if the rc does not exist or is quiescing; true otherwise. - */ -static bool uwb_rc_class_device_exists(struct uwb_rc *target_rc) -{ - struct device *dev; - - dev = class_find_device(&uwb_rc_class, NULL, target_rc, find_rc); - - put_device(dev); - - return (dev != NULL); -} - -/** - * uwb_pal_unregister - unregister a UWB PAL - * @pal: the PAL - */ -void uwb_pal_unregister(struct uwb_pal *pal) -{ - struct uwb_rc *rc = pal->rc; - - uwb_radio_stop(pal); - - mutex_lock(&rc->uwb_dev.mutex); - list_del(&pal->node); - mutex_unlock(&rc->uwb_dev.mutex); - - debugfs_remove(pal->debugfs_dir); - - if (pal->device) { - /* remove link to the PAL in the UWB device's directory. */ - if (uwb_rc_class_device_exists(rc)) - sysfs_remove_link(&rc->uwb_dev.dev.kobj, pal->name); - - /* remove link to uwb_rc in the PAL device's directory. */ - sysfs_remove_link(&pal->device->kobj, "uwb_rc"); - } -} -EXPORT_SYMBOL_GPL(uwb_pal_unregister); - -/** - * uwb_rc_pal_init - initialize the PAL related parts of a radio controller - * @rc: the radio controller - */ -void uwb_rc_pal_init(struct uwb_rc *rc) -{ - INIT_LIST_HEAD(&rc->pals); -} diff --git a/drivers/staging/uwb/radio.c b/drivers/staging/uwb/radio.c deleted file mode 100644 index 6afb75ce1b5f..000000000000 --- a/drivers/staging/uwb/radio.c +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * UWB radio (channel) management. - * - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#include -#include - -#include "uwb.h" -#include "uwb-internal.h" - - -static int uwb_radio_select_channel(struct uwb_rc *rc) -{ - /* - * Default to channel 9 (BG1, TFC1) unless the user has - * selected a specific channel or there are no active PALs. - */ - if (rc->active_pals == 0) - return -1; - if (rc->beaconing_forced) - return rc->beaconing_forced; - return 9; -} - - -/* - * Notify all active PALs that the channel has changed. - */ -static void uwb_radio_channel_changed(struct uwb_rc *rc, int channel) -{ - struct uwb_pal *pal; - - list_for_each_entry(pal, &rc->pals, node) { - if (pal->channel && channel != pal->channel) { - pal->channel = channel; - if (pal->channel_changed) - pal->channel_changed(pal, pal->channel); - } - } -} - -/* - * Change to a new channel and notify any active PALs of the new - * channel. - * - * When stopping the radio, PALs need to be notified first so they can - * terminate any active reservations. - */ -static int uwb_radio_change_channel(struct uwb_rc *rc, int channel) -{ - int ret = 0; - struct device *dev = &rc->uwb_dev.dev; - - dev_dbg(dev, "%s: channel = %d, rc->beaconing = %d\n", __func__, - channel, rc->beaconing); - - if (channel == -1) - uwb_radio_channel_changed(rc, channel); - - if (channel != rc->beaconing) { - if (rc->beaconing != -1 && channel != -1) { - /* - * FIXME: should signal the channel change - * with a Channel Change IE. - */ - ret = uwb_radio_change_channel(rc, -1); - if (ret < 0) - return ret; - } - ret = uwb_rc_beacon(rc, channel, 0); - } - - if (channel != -1) - uwb_radio_channel_changed(rc, rc->beaconing); - - return ret; -} - -/** - * uwb_radio_start - request that the radio be started - * @pal: the PAL making the request. - * - * If the radio is not already active, a suitable channel is selected - * and beacons are started. - */ -int uwb_radio_start(struct uwb_pal *pal) -{ - struct uwb_rc *rc = pal->rc; - int ret = 0; - - mutex_lock(&rc->uwb_dev.mutex); - - if (!pal->channel) { - pal->channel = -1; - rc->active_pals++; - ret = uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); - } - - mutex_unlock(&rc->uwb_dev.mutex); - return ret; -} -EXPORT_SYMBOL_GPL(uwb_radio_start); - -/** - * uwb_radio_stop - request that the radio be stopped. - * @pal: the PAL making the request. - * - * Stops the radio if no other PAL is making use of it. - */ -void uwb_radio_stop(struct uwb_pal *pal) -{ - struct uwb_rc *rc = pal->rc; - - mutex_lock(&rc->uwb_dev.mutex); - - if (pal->channel) { - rc->active_pals--; - uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); - pal->channel = 0; - } - - mutex_unlock(&rc->uwb_dev.mutex); -} -EXPORT_SYMBOL_GPL(uwb_radio_stop); - -/* - * uwb_radio_force_channel - force a specific channel to be used - * @rc: the radio controller. - * @channel: the channel to use; -1 to force the radio to stop; 0 to - * use the default channel selection algorithm. - */ -int uwb_radio_force_channel(struct uwb_rc *rc, int channel) -{ - int ret = 0; - - mutex_lock(&rc->uwb_dev.mutex); - - rc->beaconing_forced = channel; - ret = uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); - - mutex_unlock(&rc->uwb_dev.mutex); - return ret; -} - -/* - * uwb_radio_setup - setup the radio manager - * @rc: the radio controller. - * - * The radio controller is reset to ensure it's in a known state - * before it's used. - */ -int uwb_radio_setup(struct uwb_rc *rc) -{ - return uwb_rc_reset(rc); -} - -/* - * uwb_radio_reset_state - reset any radio manager state - * @rc: the radio controller. - * - * All internal radio manager state is reset to values corresponding - * to a reset radio controller. - */ -void uwb_radio_reset_state(struct uwb_rc *rc) -{ - struct uwb_pal *pal; - - mutex_lock(&rc->uwb_dev.mutex); - - list_for_each_entry(pal, &rc->pals, node) { - if (pal->channel) { - pal->channel = -1; - if (pal->channel_changed) - pal->channel_changed(pal, -1); - } - } - - rc->beaconing = -1; - rc->scanning = -1; - - mutex_unlock(&rc->uwb_dev.mutex); -} - -/* - * uwb_radio_shutdown - shutdown the radio manager - * @rc: the radio controller. - * - * The radio controller is reset. - */ -void uwb_radio_shutdown(struct uwb_rc *rc) -{ - uwb_radio_reset_state(rc); - uwb_rc_reset(rc); -} diff --git a/drivers/staging/uwb/reset.c b/drivers/staging/uwb/reset.c deleted file mode 100644 index 8fc7c14d903e..000000000000 --- a/drivers/staging/uwb/reset.c +++ /dev/null @@ -1,379 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * UWB basic command support and radio reset - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: - * - * - docs - * - * - Now we are serializing (using the uwb_dev->mutex) the command - * execution; it should be parallelized as much as possible some - * day. - */ -#include -#include -#include -#include -#include - -#include "uwb-internal.h" - -/** - * Command result codes (WUSB1.0[T8-69]) - */ -static -const char *__strerror[] = { - "success", - "failure", - "hardware failure", - "no more slots", - "beacon is too large", - "invalid parameter", - "unsupported power level", - "time out (wa) or invalid ie data (whci)", - "beacon size exceeded", - "cancelled", - "invalid state", - "invalid size", - "ack not received", - "no more asie notification", -}; - - -/** Return a string matching the given error code */ -const char *uwb_rc_strerror(unsigned code) -{ - if (code == 255) - return "time out"; - if (code >= ARRAY_SIZE(__strerror)) - return "unknown error"; - return __strerror[code]; -} - -int uwb_rc_cmd_async(struct uwb_rc *rc, const char *cmd_name, - struct uwb_rccb *cmd, size_t cmd_size, - u8 expected_type, u16 expected_event, - uwb_rc_cmd_cb_f cb, void *arg) -{ - struct device *dev = &rc->uwb_dev.dev; - struct uwb_rc_neh *neh; - int needtofree = 0; - int result; - - uwb_dev_lock(&rc->uwb_dev); /* Protect against rc->priv being removed */ - if (rc->priv == NULL) { - uwb_dev_unlock(&rc->uwb_dev); - return -ESHUTDOWN; - } - - if (rc->filter_cmd) { - needtofree = rc->filter_cmd(rc, &cmd, &cmd_size); - if (needtofree < 0 && needtofree != -ENOANO) { - dev_err(dev, "%s: filter error: %d\n", - cmd_name, needtofree); - uwb_dev_unlock(&rc->uwb_dev); - return needtofree; - } - } - - neh = uwb_rc_neh_add(rc, cmd, expected_type, expected_event, cb, arg); - if (IS_ERR(neh)) { - result = PTR_ERR(neh); - uwb_dev_unlock(&rc->uwb_dev); - goto out; - } - - result = rc->cmd(rc, cmd, cmd_size); - uwb_dev_unlock(&rc->uwb_dev); - if (result < 0) - uwb_rc_neh_rm(rc, neh); - else - uwb_rc_neh_arm(rc, neh); - uwb_rc_neh_put(neh); -out: - if (needtofree == 1) - kfree(cmd); - return result < 0 ? result : 0; -} -EXPORT_SYMBOL_GPL(uwb_rc_cmd_async); - -struct uwb_rc_cmd_done_params { - struct completion completion; - struct uwb_rceb *reply; - ssize_t reply_size; -}; - -static void uwb_rc_cmd_done(struct uwb_rc *rc, void *arg, - struct uwb_rceb *reply, ssize_t reply_size) -{ - struct uwb_rc_cmd_done_params *p = (struct uwb_rc_cmd_done_params *)arg; - - if (reply_size > 0) { - if (p->reply) - reply_size = min(p->reply_size, reply_size); - else - p->reply = kmalloc(reply_size, GFP_ATOMIC); - - if (p->reply) - memcpy(p->reply, reply, reply_size); - else - reply_size = -ENOMEM; - } - p->reply_size = reply_size; - complete(&p->completion); -} - - -/** - * Generic function for issuing commands to the Radio Control Interface - * - * @rc: UWB Radio Control descriptor - * @cmd_name: Name of the command being issued (for error messages) - * @cmd: Pointer to rccb structure containing the command; - * normally you embed this structure as the first member of - * the full command structure. - * @cmd_size: Size of the whole command buffer pointed to by @cmd. - * @reply: Pointer to where to store the reply - * @reply_size: @reply's size - * @expected_type: Expected type in the return event - * @expected_event: Expected event code in the return event - * @preply: Here a pointer to where the event data is received will - * be stored. Once done with the data, free with kfree(). - * - * This function is generic; it works for commands that return a fixed - * and known size or for commands that return a variable amount of data. - * - * If a buffer is provided, that is used, although it could be chopped - * to the maximum size of the buffer. If the buffer is NULL, then one - * be allocated in *preply with the whole contents of the reply. - * - * @rc needs to be referenced - */ -static -ssize_t __uwb_rc_cmd(struct uwb_rc *rc, const char *cmd_name, - struct uwb_rccb *cmd, size_t cmd_size, - struct uwb_rceb *reply, size_t reply_size, - u8 expected_type, u16 expected_event, - struct uwb_rceb **preply) -{ - ssize_t result = 0; - struct device *dev = &rc->uwb_dev.dev; - struct uwb_rc_cmd_done_params params; - - init_completion(¶ms.completion); - params.reply = reply; - params.reply_size = reply_size; - - result = uwb_rc_cmd_async(rc, cmd_name, cmd, cmd_size, - expected_type, expected_event, - uwb_rc_cmd_done, ¶ms); - if (result) - return result; - - wait_for_completion(¶ms.completion); - - if (preply) - *preply = params.reply; - - if (params.reply_size < 0) - dev_err(dev, "%s: confirmation event 0x%02x/%04x/%02x " - "reception failed: %d\n", cmd_name, - expected_type, expected_event, cmd->bCommandContext, - (int)params.reply_size); - return params.reply_size; -} - - -/** - * Generic function for issuing commands to the Radio Control Interface - * - * @rc: UWB Radio Control descriptor - * @cmd_name: Name of the command being issued (for error messages) - * @cmd: Pointer to rccb structure containing the command; - * normally you embed this structure as the first member of - * the full command structure. - * @cmd_size: Size of the whole command buffer pointed to by @cmd. - * @reply: Pointer to the beginning of the confirmation event - * buffer. Normally bigger than an 'struct hwarc_rceb'. - * You need to fill out reply->bEventType and reply->wEvent (in - * cpu order) as the function will use them to verify the - * confirmation event. - * @reply_size: Size of the reply buffer - * - * The function checks that the length returned in the reply is at - * least as big as @reply_size; if not, it will be deemed an error and - * -EIO returned. - * - * @rc needs to be referenced - */ -ssize_t uwb_rc_cmd(struct uwb_rc *rc, const char *cmd_name, - struct uwb_rccb *cmd, size_t cmd_size, - struct uwb_rceb *reply, size_t reply_size) -{ - struct device *dev = &rc->uwb_dev.dev; - ssize_t result; - - result = __uwb_rc_cmd(rc, cmd_name, - cmd, cmd_size, reply, reply_size, - reply->bEventType, reply->wEvent, NULL); - - if (result > 0 && result < reply_size) { - dev_err(dev, "%s: not enough data returned for decoding reply " - "(%zu bytes received vs at least %zu needed)\n", - cmd_name, result, reply_size); - result = -EIO; - } - return result; -} -EXPORT_SYMBOL_GPL(uwb_rc_cmd); - - -/** - * Generic function for issuing commands to the Radio Control - * Interface that return an unknown amount of data - * - * @rc: UWB Radio Control descriptor - * @cmd_name: Name of the command being issued (for error messages) - * @cmd: Pointer to rccb structure containing the command; - * normally you embed this structure as the first member of - * the full command structure. - * @cmd_size: Size of the whole command buffer pointed to by @cmd. - * @expected_type: Expected type in the return event - * @expected_event: Expected event code in the return event - * @preply: Here a pointer to where the event data is received will - * be stored. Once done with the data, free with kfree(). - * - * The function checks that the length returned in the reply is at - * least as big as a 'struct uwb_rceb *'; if not, it will be deemed an - * error and -EIO returned. - * - * @rc needs to be referenced - */ -ssize_t uwb_rc_vcmd(struct uwb_rc *rc, const char *cmd_name, - struct uwb_rccb *cmd, size_t cmd_size, - u8 expected_type, u16 expected_event, - struct uwb_rceb **preply) -{ - return __uwb_rc_cmd(rc, cmd_name, cmd, cmd_size, NULL, 0, - expected_type, expected_event, preply); -} -EXPORT_SYMBOL_GPL(uwb_rc_vcmd); - - -/** - * Reset a UWB Host Controller (and all radio settings) - * - * @rc: Host Controller descriptor - * @returns: 0 if ok, < 0 errno code on error - * - * We put the command on kmalloc'ed memory as some arches cannot do - * USB from the stack. The reply event is copied from an stage buffer, - * so it can be in the stack. See WUSB1.0[8.6.2.4] for more details. - */ -int uwb_rc_reset(struct uwb_rc *rc) -{ - int result = -ENOMEM; - struct uwb_rc_evt_confirm reply; - struct uwb_rccb *cmd; - size_t cmd_size = sizeof(*cmd); - - mutex_lock(&rc->uwb_dev.mutex); - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - goto error_kzalloc; - cmd->bCommandType = UWB_RC_CET_GENERAL; - cmd->wCommand = cpu_to_le16(UWB_RC_CMD_RESET); - reply.rceb.bEventType = UWB_RC_CET_GENERAL; - reply.rceb.wEvent = UWB_RC_CMD_RESET; - result = uwb_rc_cmd(rc, "RESET", cmd, cmd_size, - &reply.rceb, sizeof(reply)); - if (result < 0) - goto error_cmd; - if (reply.bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(&rc->uwb_dev.dev, - "RESET: command execution failed: %s (%d)\n", - uwb_rc_strerror(reply.bResultCode), reply.bResultCode); - result = -EIO; - } -error_cmd: - kfree(cmd); -error_kzalloc: - mutex_unlock(&rc->uwb_dev.mutex); - return result; -} - -int uwbd_msg_handle_reset(struct uwb_event *evt) -{ - struct uwb_rc *rc = evt->rc; - int ret; - - dev_info(&rc->uwb_dev.dev, "resetting radio controller\n"); - ret = rc->reset(rc); - if (ret < 0) { - dev_err(&rc->uwb_dev.dev, "failed to reset hardware: %d\n", ret); - goto error; - } - return 0; -error: - /* Nothing can be done except try the reset again. Wait a bit - to avoid reset loops during probe() or remove(). */ - msleep(1000); - uwb_rc_reset_all(rc); - return ret; -} - -/** - * uwb_rc_reset_all - request a reset of the radio controller and PALs - * @rc: the radio controller of the hardware device to be reset. - * - * The full hardware reset of the radio controller and all the PALs - * will be scheduled. - */ -void uwb_rc_reset_all(struct uwb_rc *rc) -{ - struct uwb_event *evt; - - evt = kzalloc(sizeof(struct uwb_event), GFP_ATOMIC); - if (unlikely(evt == NULL)) - return; - - evt->rc = __uwb_rc_get(rc); /* will be put by uwbd's uwbd_event_handle() */ - evt->ts_jiffies = jiffies; - evt->type = UWB_EVT_TYPE_MSG; - evt->message = UWB_EVT_MSG_RESET; - - uwbd_event_queue(evt); -} -EXPORT_SYMBOL_GPL(uwb_rc_reset_all); - -void uwb_rc_pre_reset(struct uwb_rc *rc) -{ - rc->stop(rc); - uwbd_flush(rc); - - uwb_radio_reset_state(rc); - uwb_rsv_remove_all(rc); -} -EXPORT_SYMBOL_GPL(uwb_rc_pre_reset); - -int uwb_rc_post_reset(struct uwb_rc *rc) -{ - int ret; - - ret = rc->start(rc); - if (ret) - goto out; - ret = uwb_rc_mac_addr_set(rc, &rc->uwb_dev.mac_addr); - if (ret) - goto out; - ret = uwb_rc_dev_addr_set(rc, &rc->uwb_dev.dev_addr); - if (ret) - goto out; -out: - return ret; -} -EXPORT_SYMBOL_GPL(uwb_rc_post_reset); diff --git a/drivers/staging/uwb/rsv.c b/drivers/staging/uwb/rsv.c deleted file mode 100644 index d593a41c3d8d..000000000000 --- a/drivers/staging/uwb/rsv.c +++ /dev/null @@ -1,1000 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * UWB reservation management. - * - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include - -#include "uwb.h" -#include "uwb-internal.h" - -static void uwb_rsv_timer(struct timer_list *t); - -static const char *rsv_states[] = { - [UWB_RSV_STATE_NONE] = "none ", - [UWB_RSV_STATE_O_INITIATED] = "o initiated ", - [UWB_RSV_STATE_O_PENDING] = "o pending ", - [UWB_RSV_STATE_O_MODIFIED] = "o modified ", - [UWB_RSV_STATE_O_ESTABLISHED] = "o established ", - [UWB_RSV_STATE_O_TO_BE_MOVED] = "o to be moved ", - [UWB_RSV_STATE_O_MOVE_EXPANDING] = "o move expanding", - [UWB_RSV_STATE_O_MOVE_COMBINING] = "o move combining", - [UWB_RSV_STATE_O_MOVE_REDUCING] = "o move reducing ", - [UWB_RSV_STATE_T_ACCEPTED] = "t accepted ", - [UWB_RSV_STATE_T_CONFLICT] = "t conflict ", - [UWB_RSV_STATE_T_PENDING] = "t pending ", - [UWB_RSV_STATE_T_DENIED] = "t denied ", - [UWB_RSV_STATE_T_RESIZED] = "t resized ", - [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = "t expanding acc ", - [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = "t expanding conf", - [UWB_RSV_STATE_T_EXPANDING_PENDING] = "t expanding pend", - [UWB_RSV_STATE_T_EXPANDING_DENIED] = "t expanding den ", -}; - -static const char *rsv_types[] = { - [UWB_DRP_TYPE_ALIEN_BP] = "alien-bp", - [UWB_DRP_TYPE_HARD] = "hard", - [UWB_DRP_TYPE_SOFT] = "soft", - [UWB_DRP_TYPE_PRIVATE] = "private", - [UWB_DRP_TYPE_PCA] = "pca", -}; - -bool uwb_rsv_has_two_drp_ies(struct uwb_rsv *rsv) -{ - static const bool has_two_drp_ies[] = { - [UWB_RSV_STATE_O_INITIATED] = false, - [UWB_RSV_STATE_O_PENDING] = false, - [UWB_RSV_STATE_O_MODIFIED] = false, - [UWB_RSV_STATE_O_ESTABLISHED] = false, - [UWB_RSV_STATE_O_TO_BE_MOVED] = false, - [UWB_RSV_STATE_O_MOVE_COMBINING] = false, - [UWB_RSV_STATE_O_MOVE_REDUCING] = false, - [UWB_RSV_STATE_O_MOVE_EXPANDING] = true, - [UWB_RSV_STATE_T_ACCEPTED] = false, - [UWB_RSV_STATE_T_CONFLICT] = false, - [UWB_RSV_STATE_T_PENDING] = false, - [UWB_RSV_STATE_T_DENIED] = false, - [UWB_RSV_STATE_T_RESIZED] = false, - [UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = true, - [UWB_RSV_STATE_T_EXPANDING_CONFLICT] = true, - [UWB_RSV_STATE_T_EXPANDING_PENDING] = true, - [UWB_RSV_STATE_T_EXPANDING_DENIED] = true, - }; - - return has_two_drp_ies[rsv->state]; -} - -/** - * uwb_rsv_state_str - return a string for a reservation state - * @state: the reservation state. - */ -const char *uwb_rsv_state_str(enum uwb_rsv_state state) -{ - if (state < UWB_RSV_STATE_NONE || state >= UWB_RSV_STATE_LAST) - return "unknown"; - return rsv_states[state]; -} -EXPORT_SYMBOL_GPL(uwb_rsv_state_str); - -/** - * uwb_rsv_type_str - return a string for a reservation type - * @type: the reservation type - */ -const char *uwb_rsv_type_str(enum uwb_drp_type type) -{ - if (type < UWB_DRP_TYPE_ALIEN_BP || type > UWB_DRP_TYPE_PCA) - return "invalid"; - return rsv_types[type]; -} -EXPORT_SYMBOL_GPL(uwb_rsv_type_str); - -void uwb_rsv_dump(char *text, struct uwb_rsv *rsv) -{ - struct device *dev = &rsv->rc->uwb_dev.dev; - struct uwb_dev_addr devaddr; - char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE]; - - uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr); - if (rsv->target.type == UWB_RSV_TARGET_DEV) - devaddr = rsv->target.dev->dev_addr; - else - devaddr = rsv->target.devaddr; - uwb_dev_addr_print(target, sizeof(target), &devaddr); - - dev_dbg(dev, "rsv %s %s -> %s: %s\n", - text, owner, target, uwb_rsv_state_str(rsv->state)); -} - -static void uwb_rsv_release(struct kref *kref) -{ - struct uwb_rsv *rsv = container_of(kref, struct uwb_rsv, kref); - - kfree(rsv); -} - -void uwb_rsv_get(struct uwb_rsv *rsv) -{ - kref_get(&rsv->kref); -} - -void uwb_rsv_put(struct uwb_rsv *rsv) -{ - kref_put(&rsv->kref, uwb_rsv_release); -} - -/* - * Get a free stream index for a reservation. - * - * If the target is a DevAddr (e.g., a WUSB cluster reservation) then - * the stream is allocated from a pool of per-RC stream indexes, - * otherwise a unique stream index for the target is selected. - */ -static int uwb_rsv_get_stream(struct uwb_rsv *rsv) -{ - struct uwb_rc *rc = rsv->rc; - struct device *dev = &rc->uwb_dev.dev; - unsigned long *streams_bm; - int stream; - - switch (rsv->target.type) { - case UWB_RSV_TARGET_DEV: - streams_bm = rsv->target.dev->streams; - break; - case UWB_RSV_TARGET_DEVADDR: - streams_bm = rc->uwb_dev.streams; - break; - default: - return -EINVAL; - } - - stream = find_first_zero_bit(streams_bm, UWB_NUM_STREAMS); - if (stream >= UWB_NUM_STREAMS) { - dev_err(dev, "%s: no available stream found\n", __func__); - return -EBUSY; - } - - rsv->stream = stream; - set_bit(stream, streams_bm); - - dev_dbg(dev, "get stream %d\n", rsv->stream); - - return 0; -} - -static void uwb_rsv_put_stream(struct uwb_rsv *rsv) -{ - struct uwb_rc *rc = rsv->rc; - struct device *dev = &rc->uwb_dev.dev; - unsigned long *streams_bm; - - switch (rsv->target.type) { - case UWB_RSV_TARGET_DEV: - streams_bm = rsv->target.dev->streams; - break; - case UWB_RSV_TARGET_DEVADDR: - streams_bm = rc->uwb_dev.streams; - break; - default: - return; - } - - clear_bit(rsv->stream, streams_bm); - - dev_dbg(dev, "put stream %d\n", rsv->stream); -} - -void uwb_rsv_backoff_win_timer(struct timer_list *t) -{ - struct uwb_drp_backoff_win *bow = from_timer(bow, t, timer); - struct uwb_rc *rc = container_of(bow, struct uwb_rc, bow); - struct device *dev = &rc->uwb_dev.dev; - - bow->can_reserve_extra_mases = true; - if (bow->total_expired <= 4) { - bow->total_expired++; - } else { - /* after 4 backoff window has expired we can exit from - * the backoff procedure */ - bow->total_expired = 0; - bow->window = UWB_DRP_BACKOFF_WIN_MIN >> 1; - } - dev_dbg(dev, "backoff_win_timer total_expired=%d, n=%d\n", bow->total_expired, bow->n); - - /* try to relocate all the "to be moved" relocations */ - uwb_rsv_handle_drp_avail_change(rc); -} - -void uwb_rsv_backoff_win_increment(struct uwb_rc *rc) -{ - struct uwb_drp_backoff_win *bow = &rc->bow; - struct device *dev = &rc->uwb_dev.dev; - unsigned timeout_us; - - dev_dbg(dev, "backoff_win_increment: window=%d\n", bow->window); - - bow->can_reserve_extra_mases = false; - - if((bow->window << 1) == UWB_DRP_BACKOFF_WIN_MAX) - return; - - bow->window <<= 1; - bow->n = prandom_u32() & (bow->window - 1); - dev_dbg(dev, "new_window=%d, n=%d\n", bow->window, bow->n); - - /* reset the timer associated variables */ - timeout_us = bow->n * UWB_SUPERFRAME_LENGTH_US; - bow->total_expired = 0; - mod_timer(&bow->timer, jiffies + usecs_to_jiffies(timeout_us)); -} - -static void uwb_rsv_stroke_timer(struct uwb_rsv *rsv) -{ - int sframes = UWB_MAX_LOST_BEACONS; - - /* - * Multicast reservations can become established within 1 - * super frame and should not be terminated if no response is - * received. - */ - if (rsv->state == UWB_RSV_STATE_NONE) { - sframes = 0; - } else if (rsv->is_multicast) { - if (rsv->state == UWB_RSV_STATE_O_INITIATED - || rsv->state == UWB_RSV_STATE_O_MOVE_EXPANDING - || rsv->state == UWB_RSV_STATE_O_MOVE_COMBINING - || rsv->state == UWB_RSV_STATE_O_MOVE_REDUCING) - sframes = 1; - if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED) - sframes = 0; - - } - - if (sframes > 0) { - /* - * Add an additional 2 superframes to account for the - * time to send the SET DRP IE command. - */ - unsigned timeout_us = (sframes + 2) * UWB_SUPERFRAME_LENGTH_US; - mod_timer(&rsv->timer, jiffies + usecs_to_jiffies(timeout_us)); - } else - del_timer(&rsv->timer); -} - -/* - * Update a reservations state, and schedule an update of the - * transmitted DRP IEs. - */ -static void uwb_rsv_state_update(struct uwb_rsv *rsv, - enum uwb_rsv_state new_state) -{ - rsv->state = new_state; - rsv->ie_valid = false; - - uwb_rsv_dump("SU", rsv); - - uwb_rsv_stroke_timer(rsv); - uwb_rsv_sched_update(rsv->rc); -} - -static void uwb_rsv_callback(struct uwb_rsv *rsv) -{ - if (rsv->callback) - rsv->callback(rsv); -} - -void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state) -{ - struct uwb_rsv_move *mv = &rsv->mv; - - if (rsv->state == new_state) { - switch (rsv->state) { - case UWB_RSV_STATE_O_ESTABLISHED: - case UWB_RSV_STATE_O_MOVE_EXPANDING: - case UWB_RSV_STATE_O_MOVE_COMBINING: - case UWB_RSV_STATE_O_MOVE_REDUCING: - case UWB_RSV_STATE_T_ACCEPTED: - case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: - case UWB_RSV_STATE_T_RESIZED: - case UWB_RSV_STATE_NONE: - uwb_rsv_stroke_timer(rsv); - break; - default: - /* Expecting a state transition so leave timer - as-is. */ - break; - } - return; - } - - uwb_rsv_dump("SC", rsv); - - switch (new_state) { - case UWB_RSV_STATE_NONE: - uwb_rsv_state_update(rsv, UWB_RSV_STATE_NONE); - uwb_rsv_remove(rsv); - uwb_rsv_callback(rsv); - break; - case UWB_RSV_STATE_O_INITIATED: - uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_INITIATED); - break; - case UWB_RSV_STATE_O_PENDING: - uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_PENDING); - break; - case UWB_RSV_STATE_O_MODIFIED: - /* in the companion there are the MASes to drop */ - bitmap_andnot(rsv->mas.bm, rsv->mas.bm, mv->companion_mas.bm, UWB_NUM_MAS); - uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MODIFIED); - break; - case UWB_RSV_STATE_O_ESTABLISHED: - if (rsv->state == UWB_RSV_STATE_O_MODIFIED - || rsv->state == UWB_RSV_STATE_O_MOVE_REDUCING) { - uwb_drp_avail_release(rsv->rc, &mv->companion_mas); - rsv->needs_release_companion_mas = false; - } - uwb_drp_avail_reserve(rsv->rc, &rsv->mas); - uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_ESTABLISHED); - uwb_rsv_callback(rsv); - break; - case UWB_RSV_STATE_O_MOVE_EXPANDING: - rsv->needs_release_companion_mas = true; - uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); - break; - case UWB_RSV_STATE_O_MOVE_COMBINING: - rsv->needs_release_companion_mas = false; - uwb_drp_avail_reserve(rsv->rc, &mv->companion_mas); - bitmap_or(rsv->mas.bm, rsv->mas.bm, mv->companion_mas.bm, UWB_NUM_MAS); - rsv->mas.safe += mv->companion_mas.safe; - rsv->mas.unsafe += mv->companion_mas.unsafe; - uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); - break; - case UWB_RSV_STATE_O_MOVE_REDUCING: - bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS); - rsv->needs_release_companion_mas = true; - rsv->mas.safe = mv->final_mas.safe; - rsv->mas.unsafe = mv->final_mas.unsafe; - bitmap_copy(rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS); - bitmap_copy(rsv->mas.unsafe_bm, mv->final_mas.unsafe_bm, UWB_NUM_MAS); - uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); - break; - case UWB_RSV_STATE_T_ACCEPTED: - case UWB_RSV_STATE_T_RESIZED: - rsv->needs_release_companion_mas = false; - uwb_drp_avail_reserve(rsv->rc, &rsv->mas); - uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_ACCEPTED); - uwb_rsv_callback(rsv); - break; - case UWB_RSV_STATE_T_DENIED: - uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_DENIED); - break; - case UWB_RSV_STATE_T_CONFLICT: - uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_CONFLICT); - break; - case UWB_RSV_STATE_T_PENDING: - uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_PENDING); - break; - case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: - rsv->needs_release_companion_mas = true; - uwb_drp_avail_reserve(rsv->rc, &mv->companion_mas); - uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_EXPANDING_ACCEPTED); - break; - default: - dev_err(&rsv->rc->uwb_dev.dev, "unhandled state: %s (%d)\n", - uwb_rsv_state_str(new_state), new_state); - } -} - -static void uwb_rsv_handle_timeout_work(struct work_struct *work) -{ - struct uwb_rsv *rsv = container_of(work, struct uwb_rsv, - handle_timeout_work); - struct uwb_rc *rc = rsv->rc; - - mutex_lock(&rc->rsvs_mutex); - - uwb_rsv_dump("TO", rsv); - - switch (rsv->state) { - case UWB_RSV_STATE_O_INITIATED: - if (rsv->is_multicast) { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); - goto unlock; - } - break; - case UWB_RSV_STATE_O_MOVE_EXPANDING: - if (rsv->is_multicast) { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); - goto unlock; - } - break; - case UWB_RSV_STATE_O_MOVE_COMBINING: - if (rsv->is_multicast) { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); - goto unlock; - } - break; - case UWB_RSV_STATE_O_MOVE_REDUCING: - if (rsv->is_multicast) { - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); - goto unlock; - } - break; - case UWB_RSV_STATE_O_ESTABLISHED: - if (rsv->is_multicast) - goto unlock; - break; - case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: - /* - * The time out could be for the main or of the - * companion DRP, assume it's for the companion and - * drop that first. A further time out is required to - * drop the main. - */ - uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); - uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); - goto unlock; - case UWB_RSV_STATE_NONE: - goto unlock; - default: - break; - } - - uwb_rsv_remove(rsv); - -unlock: - mutex_unlock(&rc->rsvs_mutex); -} - -static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc) -{ - struct uwb_rsv *rsv; - - rsv = kzalloc(sizeof(struct uwb_rsv), GFP_KERNEL); - if (!rsv) - return NULL; - - INIT_LIST_HEAD(&rsv->rc_node); - INIT_LIST_HEAD(&rsv->pal_node); - kref_init(&rsv->kref); - timer_setup(&rsv->timer, uwb_rsv_timer, 0); - - rsv->rc = rc; - INIT_WORK(&rsv->handle_timeout_work, uwb_rsv_handle_timeout_work); - - return rsv; -} - -/** - * uwb_rsv_create - allocate and initialize a UWB reservation structure - * @rc: the radio controller - * @cb: callback to use when the reservation completes or terminates - * @pal_priv: data private to the PAL to be passed in the callback - * - * The callback is called when the state of the reservation changes from: - * - * - pending to accepted - * - pending to denined - * - accepted to terminated - * - pending to terminated - */ -struct uwb_rsv *uwb_rsv_create(struct uwb_rc *rc, uwb_rsv_cb_f cb, void *pal_priv) -{ - struct uwb_rsv *rsv; - - rsv = uwb_rsv_alloc(rc); - if (!rsv) - return NULL; - - rsv->callback = cb; - rsv->pal_priv = pal_priv; - - return rsv; -} -EXPORT_SYMBOL_GPL(uwb_rsv_create); - -void uwb_rsv_remove(struct uwb_rsv *rsv) -{ - uwb_rsv_dump("RM", rsv); - - if (rsv->state != UWB_RSV_STATE_NONE) - uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); - - if (rsv->needs_release_companion_mas) - uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); - uwb_drp_avail_release(rsv->rc, &rsv->mas); - - if (uwb_rsv_is_owner(rsv)) - uwb_rsv_put_stream(rsv); - - uwb_dev_put(rsv->owner); - if (rsv->target.type == UWB_RSV_TARGET_DEV) - uwb_dev_put(rsv->target.dev); - - list_del_init(&rsv->rc_node); - uwb_rsv_put(rsv); -} - -/** - * uwb_rsv_destroy - free a UWB reservation structure - * @rsv: the reservation to free - * - * The reservation must already be terminated. - */ -void uwb_rsv_destroy(struct uwb_rsv *rsv) -{ - uwb_rsv_put(rsv); -} -EXPORT_SYMBOL_GPL(uwb_rsv_destroy); - -/** - * usb_rsv_establish - start a reservation establishment - * @rsv: the reservation - * - * The PAL should fill in @rsv's owner, target, type, max_mas, - * min_mas, max_interval and is_multicast fields. If the target is a - * uwb_dev it must be referenced. - * - * The reservation's callback will be called when the reservation is - * accepted, denied or times out. - */ -int uwb_rsv_establish(struct uwb_rsv *rsv) -{ - struct uwb_rc *rc = rsv->rc; - struct uwb_mas_bm available; - struct device *dev = &rc->uwb_dev.dev; - int ret; - - mutex_lock(&rc->rsvs_mutex); - ret = uwb_rsv_get_stream(rsv); - if (ret) { - dev_err(dev, "%s: uwb_rsv_get_stream failed: %d\n", - __func__, ret); - goto out; - } - - rsv->tiebreaker = prandom_u32() & 1; - /* get available mas bitmap */ - uwb_drp_available(rc, &available); - - ret = uwb_rsv_find_best_allocation(rsv, &available, &rsv->mas); - if (ret == UWB_RSV_ALLOC_NOT_FOUND) { - ret = -EBUSY; - uwb_rsv_put_stream(rsv); - dev_err(dev, "%s: uwb_rsv_find_best_allocation failed: %d\n", - __func__, ret); - goto out; - } - - ret = uwb_drp_avail_reserve_pending(rc, &rsv->mas); - if (ret != 0) { - uwb_rsv_put_stream(rsv); - dev_err(dev, "%s: uwb_drp_avail_reserve_pending failed: %d\n", - __func__, ret); - goto out; - } - - uwb_rsv_get(rsv); - list_add_tail(&rsv->rc_node, &rc->reservations); - rsv->owner = &rc->uwb_dev; - uwb_dev_get(rsv->owner); - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_INITIATED); -out: - mutex_unlock(&rc->rsvs_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(uwb_rsv_establish); - -/** - * uwb_rsv_modify - modify an already established reservation - * @rsv: the reservation to modify - * @max_mas: new maximum MAS to reserve - * @min_mas: new minimum MAS to reserve - * @max_interval: new max_interval to use - * - * FIXME: implement this once there are PALs that use it. - */ -int uwb_rsv_modify(struct uwb_rsv *rsv, int max_mas, int min_mas, int max_interval) -{ - return -ENOSYS; -} -EXPORT_SYMBOL_GPL(uwb_rsv_modify); - -/* - * move an already established reservation (rc->rsvs_mutex must to be - * taken when tis function is called) - */ -int uwb_rsv_try_move(struct uwb_rsv *rsv, struct uwb_mas_bm *available) -{ - struct uwb_rc *rc = rsv->rc; - struct uwb_drp_backoff_win *bow = &rc->bow; - struct device *dev = &rc->uwb_dev.dev; - struct uwb_rsv_move *mv; - int ret = 0; - - if (!bow->can_reserve_extra_mases) - return -EBUSY; - - mv = &rsv->mv; - - if (uwb_rsv_find_best_allocation(rsv, available, &mv->final_mas) == UWB_RSV_ALLOC_FOUND) { - - if (!bitmap_equal(rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS)) { - /* We want to move the reservation */ - bitmap_andnot(mv->companion_mas.bm, mv->final_mas.bm, rsv->mas.bm, UWB_NUM_MAS); - uwb_drp_avail_reserve_pending(rc, &mv->companion_mas); - uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); - } - } else { - dev_dbg(dev, "new allocation not found\n"); - } - - return ret; -} - -/* It will try to move every reservation in state O_ESTABLISHED giving - * to the MAS allocator algorithm an availability that is the real one - * plus the allocation already established from the reservation. */ -void uwb_rsv_handle_drp_avail_change(struct uwb_rc *rc) -{ - struct uwb_drp_backoff_win *bow = &rc->bow; - struct uwb_rsv *rsv; - struct uwb_mas_bm mas; - - if (!bow->can_reserve_extra_mases) - return; - - list_for_each_entry(rsv, &rc->reservations, rc_node) { - if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED || - rsv->state == UWB_RSV_STATE_O_TO_BE_MOVED) { - uwb_drp_available(rc, &mas); - bitmap_or(mas.bm, mas.bm, rsv->mas.bm, UWB_NUM_MAS); - uwb_rsv_try_move(rsv, &mas); - } - } - -} - -/** - * uwb_rsv_terminate - terminate an established reservation - * @rsv: the reservation to terminate - * - * A reservation is terminated by removing the DRP IE from the beacon, - * the other end will consider the reservation to be terminated when - * it does not see the DRP IE for at least mMaxLostBeacons. - * - * If applicable, the reference to the target uwb_dev will be released. - */ -void uwb_rsv_terminate(struct uwb_rsv *rsv) -{ - struct uwb_rc *rc = rsv->rc; - - mutex_lock(&rc->rsvs_mutex); - - if (rsv->state != UWB_RSV_STATE_NONE) - uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); - - mutex_unlock(&rc->rsvs_mutex); -} -EXPORT_SYMBOL_GPL(uwb_rsv_terminate); - -/** - * uwb_rsv_accept - accept a new reservation from a peer - * @rsv: the reservation - * @cb: call back for reservation changes - * @pal_priv: data to be passed in the above call back - * - * Reservation requests from peers are denied unless a PAL accepts it - * by calling this function. - * - * The PAL call uwb_rsv_destroy() for all accepted reservations before - * calling uwb_pal_unregister(). - */ -void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv) -{ - uwb_rsv_get(rsv); - - rsv->callback = cb; - rsv->pal_priv = pal_priv; - rsv->state = UWB_RSV_STATE_T_ACCEPTED; -} -EXPORT_SYMBOL_GPL(uwb_rsv_accept); - -/* - * Is a received DRP IE for this reservation? - */ -static bool uwb_rsv_match(struct uwb_rsv *rsv, struct uwb_dev *src, - struct uwb_ie_drp *drp_ie) -{ - struct uwb_dev_addr *rsv_src; - int stream; - - stream = uwb_ie_drp_stream_index(drp_ie); - - if (rsv->stream != stream) - return false; - - switch (rsv->target.type) { - case UWB_RSV_TARGET_DEVADDR: - return rsv->stream == stream; - case UWB_RSV_TARGET_DEV: - if (uwb_ie_drp_owner(drp_ie)) - rsv_src = &rsv->owner->dev_addr; - else - rsv_src = &rsv->target.dev->dev_addr; - return uwb_dev_addr_cmp(&src->dev_addr, rsv_src) == 0; - } - return false; -} - -static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc, - struct uwb_dev *src, - struct uwb_ie_drp *drp_ie) -{ - struct uwb_rsv *rsv; - struct uwb_pal *pal; - enum uwb_rsv_state state; - - rsv = uwb_rsv_alloc(rc); - if (!rsv) - return NULL; - - rsv->rc = rc; - rsv->owner = src; - uwb_dev_get(rsv->owner); - rsv->target.type = UWB_RSV_TARGET_DEV; - rsv->target.dev = &rc->uwb_dev; - uwb_dev_get(&rc->uwb_dev); - rsv->type = uwb_ie_drp_type(drp_ie); - rsv->stream = uwb_ie_drp_stream_index(drp_ie); - uwb_drp_ie_to_bm(&rsv->mas, drp_ie); - - /* - * See if any PALs are interested in this reservation. If not, - * deny the request. - */ - rsv->state = UWB_RSV_STATE_T_DENIED; - mutex_lock(&rc->uwb_dev.mutex); - list_for_each_entry(pal, &rc->pals, node) { - if (pal->new_rsv) - pal->new_rsv(pal, rsv); - if (rsv->state == UWB_RSV_STATE_T_ACCEPTED) - break; - } - mutex_unlock(&rc->uwb_dev.mutex); - - list_add_tail(&rsv->rc_node, &rc->reservations); - state = rsv->state; - rsv->state = UWB_RSV_STATE_NONE; - - /* FIXME: do something sensible here */ - if (state == UWB_RSV_STATE_T_ACCEPTED - && uwb_drp_avail_reserve_pending(rc, &rsv->mas) == -EBUSY) { - /* FIXME: do something sensible here */ - } else { - uwb_rsv_set_state(rsv, state); - } - - return rsv; -} - -/** - * uwb_rsv_get_usable_mas - get the bitmap of the usable MAS of a reservations - * @rsv: the reservation. - * @mas: returns the available MAS. - * - * The usable MAS of a reservation may be less than the negotiated MAS - * if alien BPs are present. - */ -void uwb_rsv_get_usable_mas(struct uwb_rsv *rsv, struct uwb_mas_bm *mas) -{ - bitmap_zero(mas->bm, UWB_NUM_MAS); - bitmap_andnot(mas->bm, rsv->mas.bm, rsv->rc->cnflt_alien_bitmap.bm, UWB_NUM_MAS); -} -EXPORT_SYMBOL_GPL(uwb_rsv_get_usable_mas); - -/** - * uwb_rsv_find - find a reservation for a received DRP IE. - * @rc: the radio controller - * @src: source of the DRP IE - * @drp_ie: the DRP IE - * - * If the reservation cannot be found and the DRP IE is from a peer - * attempting to establish a new reservation, create a new reservation - * and add it to the list. - */ -struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src, - struct uwb_ie_drp *drp_ie) -{ - struct uwb_rsv *rsv; - - list_for_each_entry(rsv, &rc->reservations, rc_node) { - if (uwb_rsv_match(rsv, src, drp_ie)) - return rsv; - } - - if (uwb_ie_drp_owner(drp_ie)) - return uwb_rsv_new_target(rc, src, drp_ie); - - return NULL; -} - -/* - * Go through all the reservations and check for timeouts and (if - * necessary) update their DRP IEs. - * - * FIXME: look at building the SET_DRP_IE command here rather than - * having to rescan the list in uwb_rc_send_all_drp_ie(). - */ -static bool uwb_rsv_update_all(struct uwb_rc *rc) -{ - struct uwb_rsv *rsv, *t; - bool ie_updated = false; - - list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) { - if (!rsv->ie_valid) { - uwb_drp_ie_update(rsv); - ie_updated = true; - } - } - - return ie_updated; -} - -void uwb_rsv_queue_update(struct uwb_rc *rc) -{ - unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; - - queue_delayed_work(rc->rsv_workq, &rc->rsv_update_work, usecs_to_jiffies(delay_us)); -} - -/** - * uwb_rsv_sched_update - schedule an update of the DRP IEs - * @rc: the radio controller. - * - * To improve performance and ensure correctness with [ECMA-368] the - * number of SET-DRP-IE commands that are done are limited. - * - * DRP IEs update come from two sources: DRP events from the hardware - * which all occur at the beginning of the superframe ('syncronous' - * events) and reservation establishment/termination requests from - * PALs or timers ('asynchronous' events). - * - * A delayed work ensures that all the synchronous events result in - * one SET-DRP-IE command. - * - * Additional logic (the set_drp_ie_pending and rsv_updated_postponed - * flags) will prevent an asynchrous event starting a SET-DRP-IE - * command if one is currently awaiting a response. - * - * FIXME: this does leave a window where an asynchrous event can delay - * the SET-DRP-IE for a synchronous event by one superframe. - */ -void uwb_rsv_sched_update(struct uwb_rc *rc) -{ - spin_lock_irq(&rc->rsvs_lock); - if (!delayed_work_pending(&rc->rsv_update_work)) { - if (rc->set_drp_ie_pending > 0) { - rc->set_drp_ie_pending++; - goto unlock; - } - uwb_rsv_queue_update(rc); - } -unlock: - spin_unlock_irq(&rc->rsvs_lock); -} - -/* - * Update DRP IEs and, if necessary, the DRP Availability IE and send - * the updated IEs to the radio controller. - */ -static void uwb_rsv_update_work(struct work_struct *work) -{ - struct uwb_rc *rc = container_of(work, struct uwb_rc, - rsv_update_work.work); - bool ie_updated; - - mutex_lock(&rc->rsvs_mutex); - - ie_updated = uwb_rsv_update_all(rc); - - if (!rc->drp_avail.ie_valid) { - uwb_drp_avail_ie_update(rc); - ie_updated = true; - } - - if (ie_updated && (rc->set_drp_ie_pending == 0)) - uwb_rc_send_all_drp_ie(rc); - - mutex_unlock(&rc->rsvs_mutex); -} - -static void uwb_rsv_alien_bp_work(struct work_struct *work) -{ - struct uwb_rc *rc = container_of(work, struct uwb_rc, - rsv_alien_bp_work.work); - struct uwb_rsv *rsv; - - mutex_lock(&rc->rsvs_mutex); - - list_for_each_entry(rsv, &rc->reservations, rc_node) { - if (rsv->type != UWB_DRP_TYPE_ALIEN_BP) { - uwb_rsv_callback(rsv); - } - } - - mutex_unlock(&rc->rsvs_mutex); -} - -static void uwb_rsv_timer(struct timer_list *t) -{ - struct uwb_rsv *rsv = from_timer(rsv, t, timer); - - queue_work(rsv->rc->rsv_workq, &rsv->handle_timeout_work); -} - -/** - * uwb_rsv_remove_all - remove all reservations - * @rc: the radio controller - * - * A DRP IE update is not done. - */ -void uwb_rsv_remove_all(struct uwb_rc *rc) -{ - struct uwb_rsv *rsv, *t; - - mutex_lock(&rc->rsvs_mutex); - list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) { - if (rsv->state != UWB_RSV_STATE_NONE) - uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); - del_timer_sync(&rsv->timer); - } - /* Cancel any postponed update. */ - rc->set_drp_ie_pending = 0; - mutex_unlock(&rc->rsvs_mutex); - - cancel_delayed_work_sync(&rc->rsv_update_work); - flush_workqueue(rc->rsv_workq); - - mutex_lock(&rc->rsvs_mutex); - list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) { - uwb_rsv_remove(rsv); - } - mutex_unlock(&rc->rsvs_mutex); -} - -void uwb_rsv_init(struct uwb_rc *rc) -{ - INIT_LIST_HEAD(&rc->reservations); - INIT_LIST_HEAD(&rc->cnflt_alien_list); - mutex_init(&rc->rsvs_mutex); - spin_lock_init(&rc->rsvs_lock); - INIT_DELAYED_WORK(&rc->rsv_update_work, uwb_rsv_update_work); - INIT_DELAYED_WORK(&rc->rsv_alien_bp_work, uwb_rsv_alien_bp_work); - rc->bow.can_reserve_extra_mases = true; - rc->bow.total_expired = 0; - rc->bow.window = UWB_DRP_BACKOFF_WIN_MIN >> 1; - timer_setup(&rc->bow.timer, uwb_rsv_backoff_win_timer, 0); - - bitmap_complement(rc->uwb_dev.streams, rc->uwb_dev.streams, UWB_NUM_STREAMS); -} - -int uwb_rsv_setup(struct uwb_rc *rc) -{ - char name[16]; - - snprintf(name, sizeof(name), "%s_rsvd", dev_name(&rc->uwb_dev.dev)); - rc->rsv_workq = create_singlethread_workqueue(name); - if (rc->rsv_workq == NULL) - return -ENOMEM; - - return 0; -} - -void uwb_rsv_cleanup(struct uwb_rc *rc) -{ - uwb_rsv_remove_all(rc); - destroy_workqueue(rc->rsv_workq); -} diff --git a/drivers/staging/uwb/scan.c b/drivers/staging/uwb/scan.c deleted file mode 100644 index ffc3f452302d..000000000000 --- a/drivers/staging/uwb/scan.c +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Scanning management - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - * FIXME: there are issues here on how BEACON and SCAN on USB RCI deal - * with each other. Currently seems that START_BEACON while - * SCAN_ONLY will cancel the scan, so we need to update the - * state here. Clarification request sent by email on - * 10/05/2005. - * 10/28/2005 No clear answer heard--maybe we'll hack the API - * so that when we start beaconing, if the HC is - * scanning in a mode not compatible with beaconing - * we just fail. - */ - -#include -#include -#include -#include -#include "uwb-internal.h" - - -/** - * Start/stop scanning in a radio controller - * - * @rc: UWB Radio Controller - * @channel: Channel to scan; encodings in WUSB1.0[Table 5.12] - * @type: Type of scanning to do. - * @bpst_offset: value at which to start scanning (if type == - * UWB_SCAN_ONLY_STARTTIME) - * @returns: 0 if ok, < 0 errno code on error - * - * We put the command on kmalloc'ed memory as some arches cannot do - * USB from the stack. The reply event is copied from an stage buffer, - * so it can be in the stack. See WUSB1.0[8.6.2.4] for more details. - */ -int uwb_rc_scan(struct uwb_rc *rc, - unsigned channel, enum uwb_scan_type type, - unsigned bpst_offset) -{ - int result; - struct uwb_rc_cmd_scan *cmd; - struct uwb_rc_evt_confirm reply; - - result = -ENOMEM; - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - goto error_kzalloc; - mutex_lock(&rc->uwb_dev.mutex); - cmd->rccb.bCommandType = UWB_RC_CET_GENERAL; - cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SCAN); - cmd->bChannelNumber = channel; - cmd->bScanState = type; - cmd->wStartTime = cpu_to_le16(bpst_offset); - reply.rceb.bEventType = UWB_RC_CET_GENERAL; - reply.rceb.wEvent = UWB_RC_CMD_SCAN; - result = uwb_rc_cmd(rc, "SCAN", &cmd->rccb, sizeof(*cmd), - &reply.rceb, sizeof(reply)); - if (result < 0) - goto error_cmd; - if (reply.bResultCode != UWB_RC_RES_SUCCESS) { - dev_err(&rc->uwb_dev.dev, - "SCAN: command execution failed: %s (%d)\n", - uwb_rc_strerror(reply.bResultCode), reply.bResultCode); - result = -EIO; - goto error_cmd; - } - rc->scanning = channel; - rc->scan_type = type; -error_cmd: - mutex_unlock(&rc->uwb_dev.mutex); - kfree(cmd); -error_kzalloc: - return result; -} - -/* - * Print scanning state - */ -static ssize_t uwb_rc_scan_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_rc *rc = uwb_dev->rc; - ssize_t result; - - mutex_lock(&rc->uwb_dev.mutex); - result = sprintf(buf, "%d %d\n", rc->scanning, rc->scan_type); - mutex_unlock(&rc->uwb_dev.mutex); - return result; -} - -/* - * - */ -static ssize_t uwb_rc_scan_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct uwb_dev *uwb_dev = to_uwb_dev(dev); - struct uwb_rc *rc = uwb_dev->rc; - unsigned channel; - unsigned type; - unsigned bpst_offset = 0; - ssize_t result = -EINVAL; - - result = sscanf(buf, "%u %u %u\n", &channel, &type, &bpst_offset); - if (result >= 2 && type < UWB_SCAN_TOP) - result = uwb_rc_scan(rc, channel, type, bpst_offset); - - return result < 0 ? result : size; -} - -/** Radio Control sysfs interface (declaration) */ -DEVICE_ATTR(scan, S_IRUGO | S_IWUSR, uwb_rc_scan_show, uwb_rc_scan_store); diff --git a/drivers/staging/uwb/umc-bus.c b/drivers/staging/uwb/umc-bus.c deleted file mode 100644 index 8b931f66a720..000000000000 --- a/drivers/staging/uwb/umc-bus.c +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Bus for UWB Multi-interface Controller capabilities. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include -#include -#include "include/umc.h" - -static int umc_bus_pre_reset_helper(struct device *dev, void *data) -{ - int ret = 0; - - if (dev->driver) { - struct umc_dev *umc = to_umc_dev(dev); - struct umc_driver *umc_drv = to_umc_driver(dev->driver); - - if (umc_drv->pre_reset) - ret = umc_drv->pre_reset(umc); - else - device_release_driver(dev); - } - return ret; -} - -static int umc_bus_post_reset_helper(struct device *dev, void *data) -{ - int ret = 0; - - if (dev->driver) { - struct umc_dev *umc = to_umc_dev(dev); - struct umc_driver *umc_drv = to_umc_driver(dev->driver); - - if (umc_drv->post_reset) - ret = umc_drv->post_reset(umc); - } else - ret = device_attach(dev); - - return ret; -} - -/** - * umc_controller_reset - reset the whole UMC controller - * @umc: the UMC device for the radio controller. - * - * Drivers or all capabilities of the controller will have their - * pre_reset methods called or be unbound from their device. Then all - * post_reset methods will be called or the drivers will be rebound. - * - * Radio controllers must provide pre_reset and post_reset methods and - * reset the hardware in their start method. - * - * If this is called while a probe() or remove() is in progress it - * will return -EAGAIN and not perform the reset. - */ -int umc_controller_reset(struct umc_dev *umc) -{ - struct device *parent = umc->dev.parent; - int ret = 0; - - if (!device_trylock(parent)) - return -EAGAIN; - ret = device_for_each_child(parent, parent, umc_bus_pre_reset_helper); - if (ret >= 0) - ret = device_for_each_child(parent, parent, umc_bus_post_reset_helper); - device_unlock(parent); - - return ret; -} -EXPORT_SYMBOL_GPL(umc_controller_reset); - -/** - * umc_match_pci_id - match a UMC driver to a UMC device's parent PCI device. - * @umc_drv: umc driver with match_data pointing to a zero-terminated - * table of pci_device_id's. - * @umc: umc device whose parent is to be matched. - */ -int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc) -{ - const struct pci_device_id *id_table = umc_drv->match_data; - struct pci_dev *pci; - - if (!dev_is_pci(umc->dev.parent)) - return 0; - - pci = to_pci_dev(umc->dev.parent); - return pci_match_id(id_table, pci) != NULL; -} -EXPORT_SYMBOL_GPL(umc_match_pci_id); - -static int umc_bus_rescan_helper(struct device *dev, void *data) -{ - int ret = 0; - - if (!dev->driver) - ret = device_attach(dev); - - return ret; -} - -static void umc_bus_rescan(struct device *parent) -{ - int err; - - /* - * We can't use bus_rescan_devices() here as it deadlocks when - * it tries to retake the dev->parent semaphore. - */ - err = device_for_each_child(parent, NULL, umc_bus_rescan_helper); - if (err < 0) - printk(KERN_WARNING "%s: rescan of bus failed: %d\n", - KBUILD_MODNAME, err); -} - -static int umc_bus_match(struct device *dev, struct device_driver *drv) -{ - struct umc_dev *umc = to_umc_dev(dev); - struct umc_driver *umc_driver = to_umc_driver(drv); - - if (umc->cap_id == umc_driver->cap_id) { - if (umc_driver->match) - return umc_driver->match(umc_driver, umc); - else - return 1; - } - return 0; -} - -static int umc_device_probe(struct device *dev) -{ - struct umc_dev *umc; - struct umc_driver *umc_driver; - int err; - - umc_driver = to_umc_driver(dev->driver); - umc = to_umc_dev(dev); - - get_device(dev); - err = umc_driver->probe(umc); - if (err) - put_device(dev); - else - umc_bus_rescan(dev->parent); - - return err; -} - -static int umc_device_remove(struct device *dev) -{ - struct umc_dev *umc; - struct umc_driver *umc_driver; - - umc_driver = to_umc_driver(dev->driver); - umc = to_umc_dev(dev); - - umc_driver->remove(umc); - put_device(dev); - return 0; -} - -static ssize_t capability_id_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct umc_dev *umc = to_umc_dev(dev); - - return sprintf(buf, "0x%02x\n", umc->cap_id); -} -static DEVICE_ATTR_RO(capability_id); - -static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct umc_dev *umc = to_umc_dev(dev); - - return sprintf(buf, "0x%04x\n", umc->version); -} -static DEVICE_ATTR_RO(version); - -static struct attribute *umc_dev_attrs[] = { - &dev_attr_capability_id.attr, - &dev_attr_version.attr, - NULL, -}; -ATTRIBUTE_GROUPS(umc_dev); - -struct bus_type umc_bus_type = { - .name = "umc", - .match = umc_bus_match, - .probe = umc_device_probe, - .remove = umc_device_remove, - .dev_groups = umc_dev_groups, -}; -EXPORT_SYMBOL_GPL(umc_bus_type); - -static int __init umc_bus_init(void) -{ - return bus_register(&umc_bus_type); -} -module_init(umc_bus_init); - -static void __exit umc_bus_exit(void) -{ - bus_unregister(&umc_bus_type); -} -module_exit(umc_bus_exit); - -MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus"); -MODULE_AUTHOR("Cambridge Silicon Radio Ltd."); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/uwb/umc-dev.c b/drivers/staging/uwb/umc-dev.c deleted file mode 100644 index 0c71caae00be..000000000000 --- a/drivers/staging/uwb/umc-dev.c +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * UWB Multi-interface Controller device management. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include "include/umc.h" - -static void umc_device_release(struct device *dev) -{ - struct umc_dev *umc = to_umc_dev(dev); - - kfree(umc); -} - -/** - * umc_device_create - allocate a child UMC device - * @parent: parent of the new UMC device. - * @n: index of the new device. - * - * The new UMC device will have a bus ID of the parent with '-n' - * appended. - */ -struct umc_dev *umc_device_create(struct device *parent, int n) -{ - struct umc_dev *umc; - - umc = kzalloc(sizeof(struct umc_dev), GFP_KERNEL); - if (umc) { - dev_set_name(&umc->dev, "%s-%d", dev_name(parent), n); - umc->dev.parent = parent; - umc->dev.bus = &umc_bus_type; - umc->dev.release = umc_device_release; - - umc->dev.dma_mask = parent->dma_mask; - } - return umc; -} -EXPORT_SYMBOL_GPL(umc_device_create); - -/** - * umc_device_register - register a UMC device - * @umc: pointer to the UMC device - * - * The memory resource for the UMC device is acquired and the device - * registered with the system. - */ -int umc_device_register(struct umc_dev *umc) -{ - int err; - - err = request_resource(umc->resource.parent, &umc->resource); - if (err < 0) { - dev_err(&umc->dev, "can't allocate resource range %pR: %d\n", - &umc->resource, err); - goto error_request_resource; - } - - err = device_register(&umc->dev); - if (err < 0) - goto error_device_register; - return 0; - -error_device_register: - put_device(&umc->dev); - release_resource(&umc->resource); -error_request_resource: - return err; -} -EXPORT_SYMBOL_GPL(umc_device_register); - -/** - * umc_device_unregister - unregister a UMC device - * @umc: pointer to the UMC device - * - * First we unregister the device, make sure the driver can do it's - * resource release thing and then we try to release any left over - * resources. We take a ref to the device, to make sure it doesn't - * disappear under our feet. - */ -void umc_device_unregister(struct umc_dev *umc) -{ - struct device *dev; - if (!umc) - return; - dev = get_device(&umc->dev); - device_unregister(&umc->dev); - release_resource(&umc->resource); - put_device(dev); -} -EXPORT_SYMBOL_GPL(umc_device_unregister); diff --git a/drivers/staging/uwb/umc-drv.c b/drivers/staging/uwb/umc-drv.c deleted file mode 100644 index ed3bd220e8c2..000000000000 --- a/drivers/staging/uwb/umc-drv.c +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * UWB Multi-interface Controller driver management. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include "include/umc.h" - -int __umc_driver_register(struct umc_driver *umc_drv, struct module *module, - const char *mod_name) -{ - umc_drv->driver.name = umc_drv->name; - umc_drv->driver.owner = module; - umc_drv->driver.mod_name = mod_name; - umc_drv->driver.bus = &umc_bus_type; - - return driver_register(&umc_drv->driver); -} -EXPORT_SYMBOL_GPL(__umc_driver_register); - -/** - * umc_driver_register - unregister a UMC capabiltity driver. - * @umc_drv: pointer to the driver. - */ -void umc_driver_unregister(struct umc_driver *umc_drv) -{ - driver_unregister(&umc_drv->driver); -} -EXPORT_SYMBOL_GPL(umc_driver_unregister); diff --git a/drivers/staging/uwb/uwb-debug.c b/drivers/staging/uwb/uwb-debug.c deleted file mode 100644 index dd14df219ef8..000000000000 --- a/drivers/staging/uwb/uwb-debug.c +++ /dev/null @@ -1,354 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Debug support - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - * - * FIXME: doc - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/debug-cmd.h" -#include "uwb-internal.h" - -/* - * Debug interface - * - * Per radio controller debugfs files (in uwb/uwbN/): - * - * command: Flexible command interface (see ). - * - * reservations: information on reservations. - * - * accept: Set to true (Y or 1) to accept reservation requests from - * peers. - * - * drp_avail: DRP availability information. - */ - -struct uwb_dbg { - struct uwb_pal pal; - - bool accept; - struct list_head rsvs; - - struct dentry *root_d; - struct dentry *command_f; - struct dentry *reservations_f; - struct dentry *accept_f; - struct dentry *drp_avail_f; - spinlock_t list_lock; -}; - -static struct dentry *root_dir; - -static void uwb_dbg_rsv_cb(struct uwb_rsv *rsv) -{ - struct uwb_dbg *dbg = rsv->pal_priv; - - uwb_rsv_dump("debug", rsv); - - if (rsv->state == UWB_RSV_STATE_NONE) { - spin_lock(&dbg->list_lock); - list_del(&rsv->pal_node); - spin_unlock(&dbg->list_lock); - uwb_rsv_destroy(rsv); - } -} - -static int cmd_rsv_establish(struct uwb_rc *rc, - struct uwb_dbg_cmd_rsv_establish *cmd) -{ - struct uwb_mac_addr macaddr; - struct uwb_rsv *rsv; - struct uwb_dev *target; - int ret; - - memcpy(&macaddr, cmd->target, sizeof(macaddr)); - target = uwb_dev_get_by_macaddr(rc, &macaddr); - if (target == NULL) - return -ENODEV; - - rsv = uwb_rsv_create(rc, uwb_dbg_rsv_cb, rc->dbg); - if (rsv == NULL) { - uwb_dev_put(target); - return -ENOMEM; - } - - rsv->target.type = UWB_RSV_TARGET_DEV; - rsv->target.dev = target; - rsv->type = cmd->type; - rsv->max_mas = cmd->max_mas; - rsv->min_mas = cmd->min_mas; - rsv->max_interval = cmd->max_interval; - - ret = uwb_rsv_establish(rsv); - if (ret) - uwb_rsv_destroy(rsv); - else { - spin_lock(&(rc->dbg)->list_lock); - list_add_tail(&rsv->pal_node, &rc->dbg->rsvs); - spin_unlock(&(rc->dbg)->list_lock); - } - return ret; -} - -static int cmd_rsv_terminate(struct uwb_rc *rc, - struct uwb_dbg_cmd_rsv_terminate *cmd) -{ - struct uwb_rsv *rsv, *found = NULL; - int i = 0; - - spin_lock(&(rc->dbg)->list_lock); - - list_for_each_entry(rsv, &rc->dbg->rsvs, pal_node) { - if (i == cmd->index) { - found = rsv; - uwb_rsv_get(found); - break; - } - i++; - } - - spin_unlock(&(rc->dbg)->list_lock); - - if (!found) - return -EINVAL; - - uwb_rsv_terminate(found); - uwb_rsv_put(found); - - return 0; -} - -static int cmd_ie_add(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_add) -{ - return uwb_rc_ie_add(rc, - (const struct uwb_ie_hdr *) ie_to_add->data, - ie_to_add->len); -} - -static int cmd_ie_rm(struct uwb_rc *rc, struct uwb_dbg_cmd_ie *ie_to_rm) -{ - return uwb_rc_ie_rm(rc, ie_to_rm->data[0]); -} - -static ssize_t command_write(struct file *file, const char __user *buf, - size_t len, loff_t *off) -{ - struct uwb_rc *rc = file->private_data; - struct uwb_dbg_cmd cmd; - int ret = 0; - - if (len != sizeof(struct uwb_dbg_cmd)) - return -EINVAL; - - if (copy_from_user(&cmd, buf, len) != 0) - return -EFAULT; - - switch (cmd.type) { - case UWB_DBG_CMD_RSV_ESTABLISH: - ret = cmd_rsv_establish(rc, &cmd.rsv_establish); - break; - case UWB_DBG_CMD_RSV_TERMINATE: - ret = cmd_rsv_terminate(rc, &cmd.rsv_terminate); - break; - case UWB_DBG_CMD_IE_ADD: - ret = cmd_ie_add(rc, &cmd.ie_add); - break; - case UWB_DBG_CMD_IE_RM: - ret = cmd_ie_rm(rc, &cmd.ie_rm); - break; - case UWB_DBG_CMD_RADIO_START: - ret = uwb_radio_start(&rc->dbg->pal); - break; - case UWB_DBG_CMD_RADIO_STOP: - uwb_radio_stop(&rc->dbg->pal); - break; - default: - return -EINVAL; - } - - return ret < 0 ? ret : len; -} - -static const struct file_operations command_fops = { - .open = simple_open, - .write = command_write, - .read = NULL, - .llseek = no_llseek, - .owner = THIS_MODULE, -}; - -static int reservations_show(struct seq_file *s, void *p) -{ - struct uwb_rc *rc = s->private; - struct uwb_rsv *rsv; - - mutex_lock(&rc->rsvs_mutex); - - list_for_each_entry(rsv, &rc->reservations, rc_node) { - struct uwb_dev_addr devaddr; - char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE]; - bool is_owner; - - uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr); - if (rsv->target.type == UWB_RSV_TARGET_DEV) { - devaddr = rsv->target.dev->dev_addr; - is_owner = &rc->uwb_dev == rsv->owner; - } else { - devaddr = rsv->target.devaddr; - is_owner = true; - } - uwb_dev_addr_print(target, sizeof(target), &devaddr); - - seq_printf(s, "%c %s -> %s: %s\n", - is_owner ? 'O' : 'T', - owner, target, uwb_rsv_state_str(rsv->state)); - seq_printf(s, " stream: %d type: %s\n", - rsv->stream, uwb_rsv_type_str(rsv->type)); - seq_printf(s, " %*pb\n", UWB_NUM_MAS, rsv->mas.bm); - } - - mutex_unlock(&rc->rsvs_mutex); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(reservations); - -static int drp_avail_show(struct seq_file *s, void *p) -{ - struct uwb_rc *rc = s->private; - - seq_printf(s, "global: %*pb\n", UWB_NUM_MAS, rc->drp_avail.global); - seq_printf(s, "local: %*pb\n", UWB_NUM_MAS, rc->drp_avail.local); - seq_printf(s, "pending: %*pb\n", UWB_NUM_MAS, rc->drp_avail.pending); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(drp_avail); - -static void uwb_dbg_channel_changed(struct uwb_pal *pal, int channel) -{ - struct device *dev = &pal->rc->uwb_dev.dev; - - if (channel > 0) - dev_info(dev, "debug: channel %d started\n", channel); - else - dev_info(dev, "debug: channel stopped\n"); -} - -static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv) -{ - struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal); - - if (dbg->accept) { - spin_lock(&dbg->list_lock); - list_add_tail(&rsv->pal_node, &dbg->rsvs); - spin_unlock(&dbg->list_lock); - uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, dbg); - } -} - -/** - * uwb_dbg_add_rc - add a debug interface for a radio controller - * @rc: the radio controller - */ -void uwb_dbg_add_rc(struct uwb_rc *rc) -{ - rc->dbg = kzalloc(sizeof(struct uwb_dbg), GFP_KERNEL); - if (rc->dbg == NULL) - return; - - INIT_LIST_HEAD(&rc->dbg->rsvs); - spin_lock_init(&(rc->dbg)->list_lock); - - uwb_pal_init(&rc->dbg->pal); - rc->dbg->pal.rc = rc; - rc->dbg->pal.channel_changed = uwb_dbg_channel_changed; - rc->dbg->pal.new_rsv = uwb_dbg_new_rsv; - uwb_pal_register(&rc->dbg->pal); - - if (root_dir) { - rc->dbg->root_d = debugfs_create_dir(dev_name(&rc->uwb_dev.dev), - root_dir); - rc->dbg->command_f = debugfs_create_file("command", 0200, - rc->dbg->root_d, rc, - &command_fops); - rc->dbg->reservations_f = debugfs_create_file("reservations", 0444, - rc->dbg->root_d, rc, - &reservations_fops); - rc->dbg->accept_f = debugfs_create_bool("accept", 0644, - rc->dbg->root_d, - &rc->dbg->accept); - rc->dbg->drp_avail_f = debugfs_create_file("drp_avail", 0444, - rc->dbg->root_d, rc, - &drp_avail_fops); - } -} - -/** - * uwb_dbg_del_rc - remove a radio controller's debug interface - * @rc: the radio controller - */ -void uwb_dbg_del_rc(struct uwb_rc *rc) -{ - struct uwb_rsv *rsv, *t; - - if (rc->dbg == NULL) - return; - - list_for_each_entry_safe(rsv, t, &rc->dbg->rsvs, pal_node) { - uwb_rsv_terminate(rsv); - } - - uwb_pal_unregister(&rc->dbg->pal); - - if (root_dir) { - debugfs_remove(rc->dbg->drp_avail_f); - debugfs_remove(rc->dbg->accept_f); - debugfs_remove(rc->dbg->reservations_f); - debugfs_remove(rc->dbg->command_f); - debugfs_remove(rc->dbg->root_d); - } -} - -/** - * uwb_dbg_exit - initialize the debug interface sub-module - */ -void uwb_dbg_init(void) -{ - root_dir = debugfs_create_dir("uwb", NULL); -} - -/** - * uwb_dbg_exit - clean-up the debug interface sub-module - */ -void uwb_dbg_exit(void) -{ - debugfs_remove(root_dir); -} - -/** - * uwb_dbg_create_pal_dir - create a debugfs directory for a PAL - * @pal: The PAL. - */ -struct dentry *uwb_dbg_create_pal_dir(struct uwb_pal *pal) -{ - struct uwb_rc *rc = pal->rc; - - if (root_dir && rc->dbg && rc->dbg->root_d && pal->name) - return debugfs_create_dir(pal->name, rc->dbg->root_d); - return NULL; -} diff --git a/drivers/staging/uwb/uwb-internal.h b/drivers/staging/uwb/uwb-internal.h deleted file mode 100644 index 4c2fdac7f610..000000000000 --- a/drivers/staging/uwb/uwb-internal.h +++ /dev/null @@ -1,366 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Ultra Wide Band - * UWB internal API - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This contains most of the internal API for UWB. This is stuff used - * across the stack that of course, is of no interest to the rest. - * - * Some parts might end up going public (like uwb_rc_*())... - */ - -#ifndef __UWB_INTERNAL_H__ -#define __UWB_INTERNAL_H__ - -#include -#include -#include -#include "uwb.h" - -struct uwb_beca_e; - -/* General device API */ -extern void uwb_dev_init(struct uwb_dev *uwb_dev); -extern int __uwb_dev_offair(struct uwb_dev *, struct uwb_rc *); -extern int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev, - struct uwb_rc *parent_rc); -extern void uwb_dev_rm(struct uwb_dev *uwb_dev); -extern void uwbd_dev_onair(struct uwb_rc *, struct uwb_beca_e *); -extern void uwbd_dev_offair(struct uwb_beca_e *); -void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event); - -/* General UWB Radio Controller Internal API */ -extern struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *); -static inline struct uwb_rc *__uwb_rc_get(struct uwb_rc *rc) -{ - uwb_dev_get(&rc->uwb_dev); - return rc; -} - -static inline void __uwb_rc_put(struct uwb_rc *rc) -{ - if (rc) - uwb_dev_put(&rc->uwb_dev); -} - -extern int uwb_rc_reset(struct uwb_rc *rc); -extern int uwb_rc_beacon(struct uwb_rc *rc, - int channel, unsigned bpst_offset); -extern int uwb_rc_scan(struct uwb_rc *rc, - unsigned channel, enum uwb_scan_type type, - unsigned bpst_offset); -extern int uwb_rc_send_all_drp_ie(struct uwb_rc *rc); - -void uwb_rc_ie_init(struct uwb_rc *); -int uwb_rc_ie_setup(struct uwb_rc *); -void uwb_rc_ie_release(struct uwb_rc *); -int uwb_ie_dump_hex(const struct uwb_ie_hdr *ies, size_t len, - char *buf, size_t size); -int uwb_rc_set_ie(struct uwb_rc *, struct uwb_rc_cmd_set_ie *); - - -extern const char *uwb_rc_strerror(unsigned code); - -/* - * Time to wait for a response to an RC command. - * - * Some commands can take a long time to response. e.g., START_BEACON - * may scan for several superframes before joining an existing beacon - * group and this can take around 600 ms. - */ -#define UWB_RC_CMD_TIMEOUT_MS 1000 /* ms */ - -/* - * Notification/Event Handlers - */ - -struct uwb_rc_neh; - -extern int uwb_rc_cmd_async(struct uwb_rc *rc, const char *cmd_name, - struct uwb_rccb *cmd, size_t cmd_size, - u8 expected_type, u16 expected_event, - uwb_rc_cmd_cb_f cb, void *arg); - - -void uwb_rc_neh_create(struct uwb_rc *rc); -void uwb_rc_neh_destroy(struct uwb_rc *rc); - -struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd, - u8 expected_type, u16 expected_event, - uwb_rc_cmd_cb_f cb, void *arg); -void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh); -void uwb_rc_neh_arm(struct uwb_rc *rc, struct uwb_rc_neh *neh); -void uwb_rc_neh_put(struct uwb_rc_neh *neh); - -/* Event size tables */ -extern int uwb_est_create(void); -extern void uwb_est_destroy(void); - -/* - * UWB conflicting alien reservations - */ -struct uwb_cnflt_alien { - struct uwb_rc *rc; - struct list_head rc_node; - struct uwb_mas_bm mas; - struct timer_list timer; - struct work_struct cnflt_update_work; -}; - -enum uwb_uwb_rsv_alloc_result { - UWB_RSV_ALLOC_FOUND = 0, - UWB_RSV_ALLOC_NOT_FOUND, -}; - -enum uwb_rsv_mas_status { - UWB_RSV_MAS_NOT_AVAIL = 1, - UWB_RSV_MAS_SAFE, - UWB_RSV_MAS_UNSAFE, -}; - -struct uwb_rsv_col_set_info { - unsigned char start_col; - unsigned char interval; - unsigned char safe_mas_per_col; - unsigned char unsafe_mas_per_col; -}; - -struct uwb_rsv_col_info { - unsigned char max_avail_safe; - unsigned char max_avail_unsafe; - unsigned char highest_mas[UWB_MAS_PER_ZONE]; - struct uwb_rsv_col_set_info csi; -}; - -struct uwb_rsv_row_info { - unsigned char avail[UWB_MAS_PER_ZONE]; - unsigned char free_rows; - unsigned char used_rows; -}; - -/* - * UWB find allocation - */ -struct uwb_rsv_alloc_info { - unsigned char bm[UWB_MAS_PER_ZONE * UWB_NUM_ZONES]; - struct uwb_rsv_col_info ci[UWB_NUM_ZONES]; - struct uwb_rsv_row_info ri; - struct uwb_mas_bm *not_available; - struct uwb_mas_bm *result; - int min_mas; - int max_mas; - int max_interval; - int total_allocated_mases; - int safe_allocated_mases; - int unsafe_allocated_mases; - int interval; -}; - -int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, - struct uwb_mas_bm *available, - struct uwb_mas_bm *result); -void uwb_rsv_handle_drp_avail_change(struct uwb_rc *rc); -/* - * UWB Events & management daemon - */ - -/** - * enum uwb_event_type - types of UWB management daemon events - * - * The UWB management daemon (uwbd) can receive two types of events: - * UWB_EVT_TYPE_NOTIF - notification from the radio controller. - * UWB_EVT_TYPE_MSG - a simple message. - */ -enum uwb_event_type { - UWB_EVT_TYPE_NOTIF, - UWB_EVT_TYPE_MSG, -}; - -/** - * struct uwb_event_notif - an event for a radio controller notification - * @size: Size of the buffer (ie: Guaranteed to contain at least - * a full 'struct uwb_rceb') - * @rceb: Pointer to a kmalloced() event payload - */ -struct uwb_event_notif { - size_t size; - struct uwb_rceb *rceb; -}; - -/** - * enum uwb_event_message - an event for a message for asynchronous processing - * - * UWB_EVT_MSG_RESET - reset the radio controller and all PAL hardware. - */ -enum uwb_event_message { - UWB_EVT_MSG_RESET, -}; - -/** - * UWB Event - * @rc: Radio controller that emitted the event (referenced) - * @ts_jiffies: Timestamp, when was it received - * @type: This event's type. - */ -struct uwb_event { - struct list_head list_node; - struct uwb_rc *rc; - unsigned long ts_jiffies; - enum uwb_event_type type; - union { - struct uwb_event_notif notif; - enum uwb_event_message message; - }; -}; - -extern void uwbd_start(struct uwb_rc *rc); -extern void uwbd_stop(struct uwb_rc *rc); -extern struct uwb_event *uwb_event_alloc(size_t, gfp_t gfp_mask); -extern void uwbd_event_queue(struct uwb_event *); -void uwbd_flush(struct uwb_rc *rc); - -/* UWB event handlers */ -extern int uwbd_evt_handle_rc_ie_rcv(struct uwb_event *); -extern int uwbd_evt_handle_rc_beacon(struct uwb_event *); -extern int uwbd_evt_handle_rc_beacon_size(struct uwb_event *); -extern int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *); -extern int uwbd_evt_handle_rc_bp_slot_change(struct uwb_event *); -extern int uwbd_evt_handle_rc_drp(struct uwb_event *); -extern int uwbd_evt_handle_rc_drp_avail(struct uwb_event *); - -int uwbd_msg_handle_reset(struct uwb_event *evt); - - -/* - * Address management - */ -int uwb_rc_dev_addr_assign(struct uwb_rc *rc); -int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt); - -/* - * UWB Beacon Cache - * - * Each beacon we received is kept in a cache--when we receive that - * beacon consistently, that means there is a new device that we have - * to add to the system. - */ - -extern unsigned long beacon_timeout_ms; - -/** - * Beacon cache entry - * - * @jiffies_refresh: last time a beacon was received that refreshed - * this cache entry. - * @uwb_dev: device connected to this beacon. This pointer is not - * safe, you need to get it with uwb_dev_try_get() - * - * @hits: how many time we have seen this beacon since last time we - * cleared it - */ -struct uwb_beca_e { - struct mutex mutex; - struct kref refcnt; - struct list_head node; - struct uwb_mac_addr *mac_addr; - struct uwb_dev_addr dev_addr; - u8 hits; - unsigned long ts_jiffies; - struct uwb_dev *uwb_dev; - struct uwb_rc_evt_beacon *be; - struct stats lqe_stats, rssi_stats; /* radio statistics */ -}; -struct uwb_beacon_frame; -extern ssize_t uwb_bce_print_IEs(struct uwb_dev *, struct uwb_beca_e *, - char *, size_t); - -extern void uwb_bce_kfree(struct kref *_bce); -static inline void uwb_bce_get(struct uwb_beca_e *bce) -{ - kref_get(&bce->refcnt); -} -static inline void uwb_bce_put(struct uwb_beca_e *bce) -{ - kref_put(&bce->refcnt, uwb_bce_kfree); -} -extern void uwb_beca_purge(struct uwb_rc *rc); -extern void uwb_beca_release(struct uwb_rc *rc); - -struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc, - const struct uwb_dev_addr *devaddr); -struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc, - const struct uwb_mac_addr *macaddr); - -int uwb_radio_setup(struct uwb_rc *rc); -void uwb_radio_reset_state(struct uwb_rc *rc); -void uwb_radio_shutdown(struct uwb_rc *rc); -int uwb_radio_force_channel(struct uwb_rc *rc, int channel); - -/* -- UWB Sysfs representation */ -extern struct class uwb_rc_class; -extern struct bus_type uwb_bus_type; -extern struct device_attribute dev_attr_mac_address; -extern struct device_attribute dev_attr_beacon; -extern struct device_attribute dev_attr_scan; - -/* -- DRP Bandwidth allocator: bandwidth allocations, reservations, DRP */ -void uwb_rsv_init(struct uwb_rc *rc); -int uwb_rsv_setup(struct uwb_rc *rc); -void uwb_rsv_cleanup(struct uwb_rc *rc); -void uwb_rsv_remove_all(struct uwb_rc *rc); -void uwb_rsv_get(struct uwb_rsv *rsv); -void uwb_rsv_put(struct uwb_rsv *rsv); -bool uwb_rsv_has_two_drp_ies(struct uwb_rsv *rsv); -void uwb_rsv_dump(char *text, struct uwb_rsv *rsv); -int uwb_rsv_try_move(struct uwb_rsv *rsv, struct uwb_mas_bm *available); -void uwb_rsv_backoff_win_timer(struct timer_list *t); -void uwb_rsv_backoff_win_increment(struct uwb_rc *rc); -int uwb_rsv_status(struct uwb_rsv *rsv); -int uwb_rsv_companion_status(struct uwb_rsv *rsv); - -void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state); -void uwb_rsv_remove(struct uwb_rsv *rsv); -struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src, - struct uwb_ie_drp *drp_ie); -void uwb_rsv_sched_update(struct uwb_rc *rc); -void uwb_rsv_queue_update(struct uwb_rc *rc); - -int uwb_drp_ie_update(struct uwb_rsv *rsv); -void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie); - -void uwb_drp_avail_init(struct uwb_rc *rc); -void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail); -int uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas); -void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas); -void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas); -void uwb_drp_avail_ie_update(struct uwb_rc *rc); - -/* -- PAL support */ -void uwb_rc_pal_init(struct uwb_rc *rc); - -/* -- Misc */ - -extern ssize_t uwb_mac_frame_hdr_print(char *, size_t, - const struct uwb_mac_frame_hdr *); - -/* -- Debug interface */ -void uwb_dbg_init(void); -void uwb_dbg_exit(void); -void uwb_dbg_add_rc(struct uwb_rc *rc); -void uwb_dbg_del_rc(struct uwb_rc *rc); -struct dentry *uwb_dbg_create_pal_dir(struct uwb_pal *pal); - -static inline void uwb_dev_lock(struct uwb_dev *uwb_dev) -{ - device_lock(&uwb_dev->dev); -} - -static inline void uwb_dev_unlock(struct uwb_dev *uwb_dev) -{ - device_unlock(&uwb_dev->dev); -} - -#endif /* #ifndef __UWB_INTERNAL_H__ */ diff --git a/drivers/staging/uwb/uwb.h b/drivers/staging/uwb/uwb.h deleted file mode 100644 index 6a59706ba3a0..000000000000 --- a/drivers/staging/uwb/uwb.h +++ /dev/null @@ -1,817 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Ultra Wide Band - * UWB API - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: doc: overview of the API, different parts and pointers - */ - -#ifndef __LINUX__UWB_H__ -#define __LINUX__UWB_H__ - -#include -#include -#include -#include -#include -#include -#include -#include "include/spec.h" - -struct uwb_dev; -struct uwb_beca_e; -struct uwb_rc; -struct uwb_rsv; -struct uwb_dbg; - -/** - * struct uwb_dev - a UWB Device - * @rc: UWB Radio Controller that discovered the device (kind of its - * parent). - * @bce: a beacon cache entry for this device; or NULL if the device - * is a local radio controller. - * @mac_addr: the EUI-48 address of this device. - * @dev_addr: the current DevAddr used by this device. - * @beacon_slot: the slot number the beacon is using. - * @streams: bitmap of streams allocated to reservations targeted at - * this device. For an RC, this is the streams allocated for - * reservations targeted at DevAddrs. - * - * A UWB device may either by a neighbor or part of a local radio - * controller. - */ -struct uwb_dev { - struct mutex mutex; - struct list_head list_node; - struct device dev; - struct uwb_rc *rc; /* radio controller */ - struct uwb_beca_e *bce; /* Beacon Cache Entry */ - - struct uwb_mac_addr mac_addr; - struct uwb_dev_addr dev_addr; - int beacon_slot; - DECLARE_BITMAP(streams, UWB_NUM_STREAMS); - DECLARE_BITMAP(last_availability_bm, UWB_NUM_MAS); -}; -#define to_uwb_dev(d) container_of(d, struct uwb_dev, dev) - -/** - * UWB HWA/WHCI Radio Control {Command|Event} Block context IDs - * - * RC[CE]Bs have a 'context ID' field that matches the command with - * the event received to confirm it. - * - * Maximum number of context IDs - */ -enum { UWB_RC_CTX_MAX = 256 }; - - -/** Notification chain head for UWB generated events to listeners */ -struct uwb_notifs_chain { - struct list_head list; - struct mutex mutex; -}; - -/* Beacon cache list */ -struct uwb_beca { - struct list_head list; - size_t entries; - struct mutex mutex; -}; - -/* Event handling thread. */ -struct uwbd { - int pid; - struct task_struct *task; - wait_queue_head_t wq; - struct list_head event_list; - spinlock_t event_list_lock; -}; - -/** - * struct uwb_mas_bm - a bitmap of all MAS in a superframe - * @bm: a bitmap of length #UWB_NUM_MAS - */ -struct uwb_mas_bm { - DECLARE_BITMAP(bm, UWB_NUM_MAS); - DECLARE_BITMAP(unsafe_bm, UWB_NUM_MAS); - int safe; - int unsafe; -}; - -/** - * uwb_rsv_state - UWB Reservation state. - * - * NONE - reservation is not active (no DRP IE being transmitted). - * - * Owner reservation states: - * - * INITIATED - owner has sent an initial DRP request. - * PENDING - target responded with pending Reason Code. - * MODIFIED - reservation manager is modifying an established - * reservation with a different MAS allocation. - * ESTABLISHED - the reservation has been successfully negotiated. - * - * Target reservation states: - * - * DENIED - request is denied. - * ACCEPTED - request is accepted. - * PENDING - PAL has yet to make a decision to whether to accept or - * deny. - * - * FIXME: further target states TBD. - */ -enum uwb_rsv_state { - UWB_RSV_STATE_NONE = 0, - UWB_RSV_STATE_O_INITIATED, - UWB_RSV_STATE_O_PENDING, - UWB_RSV_STATE_O_MODIFIED, - UWB_RSV_STATE_O_ESTABLISHED, - UWB_RSV_STATE_O_TO_BE_MOVED, - UWB_RSV_STATE_O_MOVE_EXPANDING, - UWB_RSV_STATE_O_MOVE_COMBINING, - UWB_RSV_STATE_O_MOVE_REDUCING, - UWB_RSV_STATE_T_ACCEPTED, - UWB_RSV_STATE_T_DENIED, - UWB_RSV_STATE_T_CONFLICT, - UWB_RSV_STATE_T_PENDING, - UWB_RSV_STATE_T_EXPANDING_ACCEPTED, - UWB_RSV_STATE_T_EXPANDING_CONFLICT, - UWB_RSV_STATE_T_EXPANDING_PENDING, - UWB_RSV_STATE_T_EXPANDING_DENIED, - UWB_RSV_STATE_T_RESIZED, - - UWB_RSV_STATE_LAST, -}; - -enum uwb_rsv_target_type { - UWB_RSV_TARGET_DEV, - UWB_RSV_TARGET_DEVADDR, -}; - -/** - * struct uwb_rsv_target - the target of a reservation. - * - * Reservations unicast and targeted at a single device - * (UWB_RSV_TARGET_DEV); or (e.g., in the case of WUSB) targeted at a - * specific (private) DevAddr (UWB_RSV_TARGET_DEVADDR). - */ -struct uwb_rsv_target { - enum uwb_rsv_target_type type; - union { - struct uwb_dev *dev; - struct uwb_dev_addr devaddr; - }; -}; - -struct uwb_rsv_move { - struct uwb_mas_bm final_mas; - struct uwb_ie_drp *companion_drp_ie; - struct uwb_mas_bm companion_mas; -}; - -/* - * Number of streams reserved for reservations targeted at DevAddrs. - */ -#define UWB_NUM_GLOBAL_STREAMS 1 - -typedef void (*uwb_rsv_cb_f)(struct uwb_rsv *rsv); - -/** - * struct uwb_rsv - a DRP reservation - * - * Data structure management: - * - * @rc: the radio controller this reservation is for - * (as target or owner) - * @rc_node: a list node for the RC - * @pal_node: a list node for the PAL - * - * Owner and target parameters: - * - * @owner: the UWB device owning this reservation - * @target: the target UWB device - * @type: reservation type - * - * Owner parameters: - * - * @max_mas: maxiumum number of MAS - * @min_mas: minimum number of MAS - * @sparsity: owner selected sparsity - * @is_multicast: true iff multicast - * - * @callback: callback function when the reservation completes - * @pal_priv: private data for the PAL making the reservation - * - * Reservation status: - * - * @status: negotiation status - * @stream: stream index allocated for this reservation - * @tiebreaker: conflict tiebreaker for this reservation - * @mas: reserved MAS - * @drp_ie: the DRP IE - * @ie_valid: true iff the DRP IE matches the reservation parameters - * - * DRP reservations are uniquely identified by the owner, target and - * stream index. However, when using a DevAddr as a target (e.g., for - * a WUSB cluster reservation) the responses may be received from - * devices with different DevAddrs. In this case, reservations are - * uniquely identified by just the stream index. A number of stream - * indexes (UWB_NUM_GLOBAL_STREAMS) are reserved for this. - */ -struct uwb_rsv { - struct uwb_rc *rc; - struct list_head rc_node; - struct list_head pal_node; - struct kref kref; - - struct uwb_dev *owner; - struct uwb_rsv_target target; - enum uwb_drp_type type; - int max_mas; - int min_mas; - int max_interval; - bool is_multicast; - - uwb_rsv_cb_f callback; - void *pal_priv; - - enum uwb_rsv_state state; - bool needs_release_companion_mas; - u8 stream; - u8 tiebreaker; - struct uwb_mas_bm mas; - struct uwb_ie_drp *drp_ie; - struct uwb_rsv_move mv; - bool ie_valid; - struct timer_list timer; - struct work_struct handle_timeout_work; -}; - -static const -struct uwb_mas_bm uwb_mas_bm_zero = { .bm = { 0 } }; - -static inline void uwb_mas_bm_copy_le(void *dst, const struct uwb_mas_bm *mas) -{ - bitmap_copy_le(dst, mas->bm, UWB_NUM_MAS); -} - -/** - * struct uwb_drp_avail - a radio controller's view of MAS usage - * @global: MAS unused by neighbors (excluding reservations targeted - * or owned by the local radio controller) or the beaon period - * @local: MAS unused by local established reservations - * @pending: MAS unused by local pending reservations - * @ie: DRP Availability IE to be included in the beacon - * @ie_valid: true iff @ie is valid and does not need to regenerated from - * @global and @local - * - * Each radio controller maintains a view of MAS usage or - * availability. MAS available for a new reservation are determined - * from the intersection of @global, @local, and @pending. - * - * The radio controller must transmit a DRP Availability IE that's the - * intersection of @global and @local. - * - * A set bit indicates the MAS is unused and available. - * - * rc->rsvs_mutex should be held before accessing this data structure. - * - * [ECMA-368] section 17.4.3. - */ -struct uwb_drp_avail { - DECLARE_BITMAP(global, UWB_NUM_MAS); - DECLARE_BITMAP(local, UWB_NUM_MAS); - DECLARE_BITMAP(pending, UWB_NUM_MAS); - struct uwb_ie_drp_avail ie; - bool ie_valid; -}; - -struct uwb_drp_backoff_win { - u8 window; - u8 n; - int total_expired; - struct timer_list timer; - bool can_reserve_extra_mases; -}; - -const char *uwb_rsv_state_str(enum uwb_rsv_state state); -const char *uwb_rsv_type_str(enum uwb_drp_type type); - -struct uwb_rsv *uwb_rsv_create(struct uwb_rc *rc, uwb_rsv_cb_f cb, - void *pal_priv); -void uwb_rsv_destroy(struct uwb_rsv *rsv); - -int uwb_rsv_establish(struct uwb_rsv *rsv); -int uwb_rsv_modify(struct uwb_rsv *rsv, - int max_mas, int min_mas, int sparsity); -void uwb_rsv_terminate(struct uwb_rsv *rsv); - -void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv); - -void uwb_rsv_get_usable_mas(struct uwb_rsv *orig_rsv, struct uwb_mas_bm *mas); - -/** - * Radio Control Interface instance - * - * - * Life cycle rules: those of the UWB Device. - * - * @index: an index number for this radio controller, as used in the - * device name. - * @version: version of protocol supported by this device - * @priv: Backend implementation; rw with uwb_dev.dev.sem taken. - * @cmd: Backend implementation to execute commands; rw and call - * only with uwb_dev.dev.sem taken. - * @reset: Hardware reset of radio controller and any PAL controllers. - * @filter: Backend implementation to manipulate data to and from device - * to be compliant to specification assumed by driver (WHCI - * 0.95). - * - * uwb_dev.dev.mutex is used to execute commands and update - * the corresponding structures; can't use a spinlock - * because rc->cmd() can sleep. - * @ies: This is a dynamically allocated array cacheing the - * IEs (settable by the host) that the beacon of this - * radio controller is currently sending. - * - * In reality, we store here the full command we set to - * the radio controller (which is basically a command - * prefix followed by all the IEs the beacon currently - * contains). This way we don't have to realloc and - * memcpy when setting it. - * - * We set this up in uwb_rc_ie_setup(), where we alloc - * this struct, call get_ie() [so we know which IEs are - * currently being sent, if any]. - * - * @ies_capacity:Amount of space (in bytes) allocated in @ies. The - * amount used is given by sizeof(*ies) plus ies->wIELength - * (which is a little endian quantity all the time). - * @ies_mutex: protect the IE cache - * @dbg: information for the debug interface - */ -struct uwb_rc { - struct uwb_dev uwb_dev; - int index; - u16 version; - - struct module *owner; - void *priv; - int (*start)(struct uwb_rc *rc); - void (*stop)(struct uwb_rc *rc); - int (*cmd)(struct uwb_rc *, const struct uwb_rccb *, size_t); - int (*reset)(struct uwb_rc *rc); - int (*filter_cmd)(struct uwb_rc *, struct uwb_rccb **, size_t *); - int (*filter_event)(struct uwb_rc *, struct uwb_rceb **, const size_t, - size_t *, size_t *); - - spinlock_t neh_lock; /* protects neh_* and ctx_* */ - struct list_head neh_list; /* Open NE handles */ - unsigned long ctx_bm[UWB_RC_CTX_MAX / 8 / sizeof(unsigned long)]; - u8 ctx_roll; - - int beaconing; /* Beaconing state [channel number] */ - int beaconing_forced; - int scanning; - enum uwb_scan_type scan_type:3; - unsigned ready:1; - struct uwb_notifs_chain notifs_chain; - struct uwb_beca uwb_beca; - - struct uwbd uwbd; - - struct uwb_drp_backoff_win bow; - struct uwb_drp_avail drp_avail; - struct list_head reservations; - struct list_head cnflt_alien_list; - struct uwb_mas_bm cnflt_alien_bitmap; - struct mutex rsvs_mutex; - spinlock_t rsvs_lock; - struct workqueue_struct *rsv_workq; - - struct delayed_work rsv_update_work; - struct delayed_work rsv_alien_bp_work; - int set_drp_ie_pending; - struct mutex ies_mutex; - struct uwb_rc_cmd_set_ie *ies; - size_t ies_capacity; - - struct list_head pals; - int active_pals; - - struct uwb_dbg *dbg; -}; - - -/** - * struct uwb_pal - a UWB PAL - * @name: descriptive name for this PAL (wusbhc, wlp, etc.). - * @device: a device for the PAL. Used to link the PAL and the radio - * controller in sysfs. - * @rc: the radio controller the PAL uses. - * @channel_changed: called when the channel used by the radio changes. - * A channel of -1 means the channel has been stopped. - * @new_rsv: called when a peer requests a reservation (may be NULL if - * the PAL cannot accept reservation requests). - * @channel: channel being used by the PAL; 0 if the PAL isn't using - * the radio; -1 if the PAL wishes to use the radio but - * cannot. - * @debugfs_dir: a debugfs directory which the PAL can use for its own - * debugfs files. - * - * A Protocol Adaptation Layer (PAL) is a user of the WiMedia UWB - * radio platform (e.g., WUSB, WLP or Bluetooth UWB AMP). - * - * The PALs using a radio controller must register themselves to - * permit the UWB stack to coordinate usage of the radio between the - * various PALs or to allow PALs to response to certain requests from - * peers. - * - * A struct uwb_pal should be embedded in a containing structure - * belonging to the PAL and initialized with uwb_pal_init()). Fields - * should be set appropriately by the PAL before registering the PAL - * with uwb_pal_register(). - */ -struct uwb_pal { - struct list_head node; - const char *name; - struct device *device; - struct uwb_rc *rc; - - void (*channel_changed)(struct uwb_pal *pal, int channel); - void (*new_rsv)(struct uwb_pal *pal, struct uwb_rsv *rsv); - - int channel; - struct dentry *debugfs_dir; -}; - -void uwb_pal_init(struct uwb_pal *pal); -int uwb_pal_register(struct uwb_pal *pal); -void uwb_pal_unregister(struct uwb_pal *pal); - -int uwb_radio_start(struct uwb_pal *pal); -void uwb_radio_stop(struct uwb_pal *pal); - -/* - * General public API - * - * This API can be used by UWB device drivers or by those implementing - * UWB Radio Controllers - */ -struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc, - const struct uwb_dev_addr *devaddr); -struct uwb_dev *uwb_dev_get_by_rc(struct uwb_dev *, struct uwb_rc *); -static inline void uwb_dev_get(struct uwb_dev *uwb_dev) -{ - get_device(&uwb_dev->dev); -} -static inline void uwb_dev_put(struct uwb_dev *uwb_dev) -{ - put_device(&uwb_dev->dev); -} -struct uwb_dev *uwb_dev_try_get(struct uwb_rc *rc, struct uwb_dev *uwb_dev); - -/** - * Callback function for 'uwb_{dev,rc}_foreach()'. - * - * @dev: Linux device instance - * 'uwb_dev = container_of(dev, struct uwb_dev, dev)' - * @priv: Data passed by the caller to 'uwb_{dev,rc}_foreach()'. - * - * @returns: 0 to continue the iterations, any other val to stop - * iterating and return the value to the caller of - * _foreach(). - */ -typedef int (*uwb_dev_for_each_f)(struct device *dev, void *priv); -int uwb_dev_for_each(struct uwb_rc *rc, uwb_dev_for_each_f func, void *priv); - -struct uwb_rc *uwb_rc_alloc(void); -struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *); -struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *); -void uwb_rc_put(struct uwb_rc *rc); - -typedef void (*uwb_rc_cmd_cb_f)(struct uwb_rc *rc, void *arg, - struct uwb_rceb *reply, ssize_t reply_size); - -int uwb_rc_cmd_async(struct uwb_rc *rc, const char *cmd_name, - struct uwb_rccb *cmd, size_t cmd_size, - u8 expected_type, u16 expected_event, - uwb_rc_cmd_cb_f cb, void *arg); -ssize_t uwb_rc_cmd(struct uwb_rc *rc, const char *cmd_name, - struct uwb_rccb *cmd, size_t cmd_size, - struct uwb_rceb *reply, size_t reply_size); -ssize_t uwb_rc_vcmd(struct uwb_rc *rc, const char *cmd_name, - struct uwb_rccb *cmd, size_t cmd_size, - u8 expected_type, u16 expected_event, - struct uwb_rceb **preply); - -size_t __uwb_addr_print(char *, size_t, const unsigned char *, int); - -int uwb_rc_dev_addr_set(struct uwb_rc *, const struct uwb_dev_addr *); -int uwb_rc_dev_addr_get(struct uwb_rc *, struct uwb_dev_addr *); -int uwb_rc_mac_addr_set(struct uwb_rc *, const struct uwb_mac_addr *); -int uwb_rc_mac_addr_get(struct uwb_rc *, struct uwb_mac_addr *); -int __uwb_mac_addr_assigned_check(struct device *, void *); -int __uwb_dev_addr_assigned_check(struct device *, void *); - -/* Print in @buf a pretty repr of @addr */ -static inline size_t uwb_dev_addr_print(char *buf, size_t buf_size, - const struct uwb_dev_addr *addr) -{ - return __uwb_addr_print(buf, buf_size, addr->data, 0); -} - -/* Print in @buf a pretty repr of @addr */ -static inline size_t uwb_mac_addr_print(char *buf, size_t buf_size, - const struct uwb_mac_addr *addr) -{ - return __uwb_addr_print(buf, buf_size, addr->data, 1); -} - -/* @returns 0 if device addresses @addr2 and @addr1 are equal */ -static inline int uwb_dev_addr_cmp(const struct uwb_dev_addr *addr1, - const struct uwb_dev_addr *addr2) -{ - return memcmp(addr1, addr2, sizeof(*addr1)); -} - -/* @returns 0 if MAC addresses @addr2 and @addr1 are equal */ -static inline int uwb_mac_addr_cmp(const struct uwb_mac_addr *addr1, - const struct uwb_mac_addr *addr2) -{ - return memcmp(addr1, addr2, sizeof(*addr1)); -} - -/* @returns !0 if a MAC @addr is a broadcast address */ -static inline int uwb_mac_addr_bcast(const struct uwb_mac_addr *addr) -{ - struct uwb_mac_addr bcast = { - .data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } - }; - return !uwb_mac_addr_cmp(addr, &bcast); -} - -/* @returns !0 if a MAC @addr is all zeroes*/ -static inline int uwb_mac_addr_unset(const struct uwb_mac_addr *addr) -{ - struct uwb_mac_addr unset = { - .data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } - }; - return !uwb_mac_addr_cmp(addr, &unset); -} - -/* @returns !0 if the address is in use. */ -static inline unsigned __uwb_dev_addr_assigned(struct uwb_rc *rc, - struct uwb_dev_addr *addr) -{ - return uwb_dev_for_each(rc, __uwb_dev_addr_assigned_check, addr); -} - -/* - * UWB Radio Controller API - * - * This API is used (in addition to the general API) to implement UWB - * Radio Controllers. - */ -void uwb_rc_init(struct uwb_rc *); -int uwb_rc_add(struct uwb_rc *, struct device *dev, void *rc_priv); -void uwb_rc_rm(struct uwb_rc *); -void uwb_rc_neh_grok(struct uwb_rc *, void *, size_t); -void uwb_rc_neh_error(struct uwb_rc *, int); -void uwb_rc_reset_all(struct uwb_rc *rc); -void uwb_rc_pre_reset(struct uwb_rc *rc); -int uwb_rc_post_reset(struct uwb_rc *rc); - -/** - * uwb_rsv_is_owner - is the owner of this reservation the RC? - * @rsv: the reservation - */ -static inline bool uwb_rsv_is_owner(struct uwb_rsv *rsv) -{ - return rsv->owner == &rsv->rc->uwb_dev; -} - -/** - * enum uwb_notifs - UWB events that can be passed to any listeners - * @UWB_NOTIF_ONAIR: a new neighbour has joined the beacon group. - * @UWB_NOTIF_OFFAIR: a neighbour has left the beacon group. - * - * Higher layers can register callback functions with the radio - * controller using uwb_notifs_register(). The radio controller - * maintains a list of all registered handlers and will notify all - * nodes when an event occurs. - */ -enum uwb_notifs { - UWB_NOTIF_ONAIR, - UWB_NOTIF_OFFAIR, -}; - -/* Callback function registered with UWB */ -struct uwb_notifs_handler { - struct list_head list_node; - void (*cb)(void *, struct uwb_dev *, enum uwb_notifs); - void *data; -}; - -int uwb_notifs_register(struct uwb_rc *, struct uwb_notifs_handler *); -int uwb_notifs_deregister(struct uwb_rc *, struct uwb_notifs_handler *); - - -/** - * UWB radio controller Event Size Entry (for creating entry tables) - * - * WUSB and WHCI define events and notifications, and they might have - * fixed or variable size. - * - * Each event/notification has a size which is not necessarily known - * in advance based on the event code. As well, vendor specific - * events/notifications will have a size impossible to determine - * unless we know about the device's specific details. - * - * It was way too smart of the spec writers not to think that it would - * be impossible for a generic driver to skip over vendor specific - * events/notifications if there are no LENGTH fields in the HEADER of - * each message...the transaction size cannot be counted on as the - * spec does not forbid to pack more than one event in a single - * transaction. - * - * Thus, we guess sizes with tables (or for events, when you know the - * size ahead of time you can use uwb_rc_neh_extra_size*()). We - * register tables with the known events and their sizes, and then we - * traverse those tables. For those with variable length, we provide a - * way to lookup the size inside the event/notification's - * payload. This allows device-specific event size tables to be - * registered. - * - * @size: Size of the payload - * - * @offset: if != 0, at offset @offset-1 starts a field with a length - * that has to be added to @size. The format of the field is - * given by @type. - * - * @type: Type and length of the offset field. Most common is LE 16 - * bits (that's why that is zero); others are there mostly to - * cover for bugs and weirdos. - */ -struct uwb_est_entry { - size_t size; - unsigned offset; - enum { UWB_EST_16 = 0, UWB_EST_8 = 1 } type; -}; - -int uwb_est_register(u8 type, u8 code_high, u16 vendor, u16 product, - const struct uwb_est_entry *, size_t entries); -int uwb_est_unregister(u8 type, u8 code_high, u16 vendor, u16 product, - const struct uwb_est_entry *, size_t entries); -ssize_t uwb_est_find_size(struct uwb_rc *rc, const struct uwb_rceb *rceb, - size_t len); - -/* -- Misc */ - -enum { - EDC_MAX_ERRORS = 10, - EDC_ERROR_TIMEFRAME = HZ, -}; - -/* error density counter */ -struct edc { - unsigned long timestart; - u16 errorcount; -}; - -static inline -void edc_init(struct edc *edc) -{ - edc->timestart = jiffies; -} - -/* Called when an error occurred. - * This is way to determine if the number of acceptable errors per time - * period has been exceeded. It is not accurate as there are cases in which - * this scheme will not work, for example if there are periodic occurrences - * of errors that straddle updates to the start time. This scheme is - * sufficient for our usage. - * - * @returns 1 if maximum acceptable errors per timeframe has been exceeded. - */ -static inline int edc_inc(struct edc *err_hist, u16 max_err, u16 timeframe) -{ - unsigned long now; - - now = jiffies; - if (now - err_hist->timestart > timeframe) { - err_hist->errorcount = 1; - err_hist->timestart = now; - } else if (++err_hist->errorcount > max_err) { - err_hist->errorcount = 0; - err_hist->timestart = now; - return 1; - } - return 0; -} - - -/* Information Element handling */ - -struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len); -int uwb_rc_ie_add(struct uwb_rc *uwb_rc, const struct uwb_ie_hdr *ies, size_t size); -int uwb_rc_ie_rm(struct uwb_rc *uwb_rc, enum uwb_ie element_id); - -/* - * Transmission statistics - * - * UWB uses LQI and RSSI (one byte values) for reporting radio signal - * strength and line quality indication. We do quick and dirty - * averages of those. They are signed values, btw. - * - * For 8 bit quantities, we keep the min, the max, an accumulator - * (@sigma) and a # of samples. When @samples gets to 255, we compute - * the average (@sigma / @samples), place it in @sigma and reset - * @samples to 1 (so we use it as the first sample). - * - * Now, statistically speaking, probably I am kicking the kidneys of - * some books I have in my shelves collecting dust, but I just want to - * get an approx, not the Nobel. - * - * LOCKING: there is no locking per se, but we try to keep a lockless - * schema. Only _add_samples() modifies the values--as long as you - * have other locking on top that makes sure that no two calls of - * _add_sample() happen at the same time, then we are fine. Now, for - * resetting the values we just set @samples to 0 and that makes the - * next _add_sample() to start with defaults. Reading the values in - * _show() currently can race, so you need to make sure the calls are - * under the same lock that protects calls to _add_sample(). FIXME: - * currently unlocked (It is not ultraprecise but does the trick. Bite - * me). - */ -struct stats { - s8 min, max; - s16 sigma; - atomic_t samples; -}; - -static inline -void stats_init(struct stats *stats) -{ - atomic_set(&stats->samples, 0); - wmb(); -} - -static inline -void stats_add_sample(struct stats *stats, s8 sample) -{ - s8 min, max; - s16 sigma; - unsigned samples = atomic_read(&stats->samples); - if (samples == 0) { /* it was zero before, so we initialize */ - min = 127; - max = -128; - sigma = 0; - } else { - min = stats->min; - max = stats->max; - sigma = stats->sigma; - } - - if (sample < min) /* compute new values */ - min = sample; - else if (sample > max) - max = sample; - sigma += sample; - - stats->min = min; /* commit */ - stats->max = max; - stats->sigma = sigma; - if (atomic_add_return(1, &stats->samples) > 255) { - /* wrapped around! reset */ - stats->sigma = sigma / 256; - atomic_set(&stats->samples, 1); - } -} - -static inline ssize_t stats_show(struct stats *stats, char *buf) -{ - int min, max, avg; - int samples = atomic_read(&stats->samples); - if (samples == 0) - min = max = avg = 0; - else { - min = stats->min; - max = stats->max; - avg = stats->sigma / samples; - } - return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", min, max, avg); -} - -static inline ssize_t stats_store(struct stats *stats, const char *buf, - size_t size) -{ - stats_init(stats); - return size; -} - -#endif /* #ifndef __LINUX__UWB_H__ */ diff --git a/drivers/staging/uwb/uwbd.c b/drivers/staging/uwb/uwbd.c deleted file mode 100644 index dc5c743d4f5f..000000000000 --- a/drivers/staging/uwb/uwbd.c +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Ultra Wide Band - * Neighborhood Management Daemon - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This daemon takes care of maintaing information that describes the - * UWB neighborhood that the radios in this machine can see. It also - * keeps a tab of which devices are visible, makes sure each HC sits - * on a different channel to avoid interfering, etc. - * - * Different drivers (radio controller, device, any API in general) - * communicate with this daemon through an event queue. Daemon wakes - * up, takes a list of events and handles them one by one; handling - * function is extracted from a table based on the event's type and - * subtype. Events are freed only if the handling function says so. - * - * . Lock protecting the event list has to be an spinlock and locked - * with IRQSAVE because it might be called from an interrupt - * context (ie: when events arrive and the notification drops - * down from the ISR). - * - * . UWB radio controller drivers queue events to the daemon using - * uwbd_event_queue(). They just get the event, chew it to make it - * look like UWBD likes it and pass it in a buffer allocated with - * uwb_event_alloc(). - * - * EVENTS - * - * Events have a type, a subtype, a length, some other stuff and the - * data blob, which depends on the event. The header is 'struct - * uwb_event'; for payloads, see 'struct uwbd_evt_*'. - * - * EVENT HANDLER TABLES - * - * To find a handling function for an event, the type is used to index - * a subtype-table in the type-table. The subtype-table is indexed - * with the subtype to get the function that handles the event. Start - * with the main type-table 'uwbd_evt_type_handler'. - * - * DEVICES - * - * Devices are created when a bunch of beacons have been received and - * it is stablished that the device has stable radio presence. CREATED - * only, not configured. Devices are ONLY configured when an - * Application-Specific IE Probe is receieved, in which the device - * declares which Protocol ID it groks. Then the device is CONFIGURED - * (and the driver->probe() stuff of the device model is invoked). - * - * Devices are considered disconnected when a certain number of - * beacons are not received in an amount of time. - * - * Handler functions are called normally uwbd_evt_handle_*(). - */ -#include -#include -#include -#include - -#include "uwb-internal.h" - -/* - * UWBD Event handler function signature - * - * Return !0 if the event needs not to be freed (ie the handler - * takes/took care of it). 0 means the daemon code will free the - * event. - * - * @evt->rc is already referenced and guaranteed to exist. See - * uwb_evt_handle(). - */ -typedef int (*uwbd_evt_handler_f)(struct uwb_event *); - -/** - * Properties of a UWBD event - * - * @handler: the function that will handle this event - * @name: text name of event - */ -struct uwbd_event { - uwbd_evt_handler_f handler; - const char *name; -}; - -/* Table of handlers for and properties of the UWBD Radio Control Events */ -static struct uwbd_event uwbd_urc_events[] = { - [UWB_RC_EVT_IE_RCV] = { - .handler = uwbd_evt_handle_rc_ie_rcv, - .name = "IE_RECEIVED" - }, - [UWB_RC_EVT_BEACON] = { - .handler = uwbd_evt_handle_rc_beacon, - .name = "BEACON_RECEIVED" - }, - [UWB_RC_EVT_BEACON_SIZE] = { - .handler = uwbd_evt_handle_rc_beacon_size, - .name = "BEACON_SIZE_CHANGE" - }, - [UWB_RC_EVT_BPOIE_CHANGE] = { - .handler = uwbd_evt_handle_rc_bpoie_change, - .name = "BPOIE_CHANGE" - }, - [UWB_RC_EVT_BP_SLOT_CHANGE] = { - .handler = uwbd_evt_handle_rc_bp_slot_change, - .name = "BP_SLOT_CHANGE" - }, - [UWB_RC_EVT_DRP_AVAIL] = { - .handler = uwbd_evt_handle_rc_drp_avail, - .name = "DRP_AVAILABILITY_CHANGE" - }, - [UWB_RC_EVT_DRP] = { - .handler = uwbd_evt_handle_rc_drp, - .name = "DRP" - }, - [UWB_RC_EVT_DEV_ADDR_CONFLICT] = { - .handler = uwbd_evt_handle_rc_dev_addr_conflict, - .name = "DEV_ADDR_CONFLICT", - }, -}; - - - -struct uwbd_evt_type_handler { - const char *name; - struct uwbd_event *uwbd_events; - size_t size; -}; - -/* Table of handlers for each UWBD Event type. */ -static struct uwbd_evt_type_handler uwbd_urc_evt_type_handlers[] = { - [UWB_RC_CET_GENERAL] = { - .name = "URC", - .uwbd_events = uwbd_urc_events, - .size = ARRAY_SIZE(uwbd_urc_events), - }, -}; - -static const struct uwbd_event uwbd_message_handlers[] = { - [UWB_EVT_MSG_RESET] = { - .handler = uwbd_msg_handle_reset, - .name = "reset", - }, -}; - -/* - * Handle an URC event passed to the UWB Daemon - * - * @evt: the event to handle - * @returns: 0 if the event can be kfreed, !0 on the contrary - * (somebody else took ownership) [coincidentally, returning - * a <0 errno code will free it :)]. - * - * Looks up the two indirection tables (one for the type, one for the - * subtype) to decide which function handles it and then calls the - * handler. - * - * The event structure passed to the event handler has the radio - * controller in @evt->rc referenced. The reference will be dropped - * once the handler returns, so if it needs it for longer (async), - * it'll need to take another one. - */ -static -int uwbd_event_handle_urc(struct uwb_event *evt) -{ - int result = -EINVAL; - struct uwbd_evt_type_handler *type_table; - uwbd_evt_handler_f handler; - u8 type, context; - u16 event; - - type = evt->notif.rceb->bEventType; - event = le16_to_cpu(evt->notif.rceb->wEvent); - context = evt->notif.rceb->bEventContext; - - if (type >= ARRAY_SIZE(uwbd_urc_evt_type_handlers)) - goto out; - type_table = &uwbd_urc_evt_type_handlers[type]; - if (type_table->uwbd_events == NULL) - goto out; - if (event >= type_table->size) - goto out; - handler = type_table->uwbd_events[event].handler; - if (handler == NULL) - goto out; - - result = (*handler)(evt); -out: - if (result < 0) - dev_err(&evt->rc->uwb_dev.dev, - "UWBD: event 0x%02x/%04x/%02x, handling failed: %d\n", - type, event, context, result); - return result; -} - -static void uwbd_event_handle_message(struct uwb_event *evt) -{ - struct uwb_rc *rc; - int result; - - rc = evt->rc; - - if (evt->message < 0 || evt->message >= ARRAY_SIZE(uwbd_message_handlers)) { - dev_err(&rc->uwb_dev.dev, "UWBD: invalid message type %d\n", evt->message); - return; - } - - result = uwbd_message_handlers[evt->message].handler(evt); - if (result < 0) - dev_err(&rc->uwb_dev.dev, "UWBD: '%s' message failed: %d\n", - uwbd_message_handlers[evt->message].name, result); -} - -static void uwbd_event_handle(struct uwb_event *evt) -{ - struct uwb_rc *rc; - int should_keep; - - rc = evt->rc; - - if (rc->ready) { - switch (evt->type) { - case UWB_EVT_TYPE_NOTIF: - should_keep = uwbd_event_handle_urc(evt); - if (should_keep <= 0) - kfree(evt->notif.rceb); - break; - case UWB_EVT_TYPE_MSG: - uwbd_event_handle_message(evt); - break; - default: - dev_err(&rc->uwb_dev.dev, "UWBD: invalid event type %d\n", evt->type); - break; - } - } - - __uwb_rc_put(rc); /* for the __uwb_rc_get() in uwb_rc_notif_cb() */ -} - -/** - * UWB Daemon - * - * Listens to all UWB notifications and takes care to track the state - * of the UWB neighbourhood for the kernel. When we do a run, we - * spinlock, move the list to a private copy and release the - * lock. Hold it as little as possible. Not a conflict: it is - * guaranteed we own the events in the private list. - * - * FIXME: should change so we don't have a 1HZ timer all the time, but - * only if there are devices. - */ -static int uwbd(void *param) -{ - struct uwb_rc *rc = param; - unsigned long flags; - struct uwb_event *evt; - int should_stop = 0; - - while (1) { - wait_event_interruptible_timeout( - rc->uwbd.wq, - !list_empty(&rc->uwbd.event_list) - || (should_stop = kthread_should_stop()), - HZ); - if (should_stop) - break; - - spin_lock_irqsave(&rc->uwbd.event_list_lock, flags); - if (!list_empty(&rc->uwbd.event_list)) { - evt = list_first_entry(&rc->uwbd.event_list, struct uwb_event, list_node); - list_del(&evt->list_node); - } else - evt = NULL; - spin_unlock_irqrestore(&rc->uwbd.event_list_lock, flags); - - if (evt) { - uwbd_event_handle(evt); - kfree(evt); - } - - uwb_beca_purge(rc); /* Purge devices that left */ - } - return 0; -} - - -/** Start the UWB daemon */ -void uwbd_start(struct uwb_rc *rc) -{ - struct task_struct *task = kthread_run(uwbd, rc, "uwbd"); - if (IS_ERR(task)) { - rc->uwbd.task = NULL; - printk(KERN_ERR "UWB: Cannot start management daemon; " - "UWB won't work\n"); - } else { - rc->uwbd.task = task; - rc->uwbd.pid = rc->uwbd.task->pid; - } -} - -/* Stop the UWB daemon and free any unprocessed events */ -void uwbd_stop(struct uwb_rc *rc) -{ - if (rc->uwbd.task) - kthread_stop(rc->uwbd.task); - uwbd_flush(rc); -} - -/* - * Queue an event for the management daemon - * - * When some lower layer receives an event, it uses this function to - * push it forward to the UWB daemon. - * - * Once you pass the event, you don't own it any more, but the daemon - * does. It will uwb_event_free() it when done, so make sure you - * uwb_event_alloc()ed it or bad things will happen. - * - * If the daemon is not running, we just free the event. - */ -void uwbd_event_queue(struct uwb_event *evt) -{ - struct uwb_rc *rc = evt->rc; - unsigned long flags; - - spin_lock_irqsave(&rc->uwbd.event_list_lock, flags); - if (rc->uwbd.pid != 0) { - list_add(&evt->list_node, &rc->uwbd.event_list); - wake_up_all(&rc->uwbd.wq); - } else { - __uwb_rc_put(evt->rc); - if (evt->type == UWB_EVT_TYPE_NOTIF) - kfree(evt->notif.rceb); - kfree(evt); - } - spin_unlock_irqrestore(&rc->uwbd.event_list_lock, flags); - return; -} - -void uwbd_flush(struct uwb_rc *rc) -{ - struct uwb_event *evt, *nxt; - - spin_lock_irq(&rc->uwbd.event_list_lock); - list_for_each_entry_safe(evt, nxt, &rc->uwbd.event_list, list_node) { - if (evt->rc == rc) { - __uwb_rc_put(rc); - list_del(&evt->list_node); - if (evt->type == UWB_EVT_TYPE_NOTIF) - kfree(evt->notif.rceb); - kfree(evt); - } - } - spin_unlock_irq(&rc->uwbd.event_list_lock); -} diff --git a/drivers/staging/uwb/whc-rc.c b/drivers/staging/uwb/whc-rc.c deleted file mode 100644 index a5ab255d7d36..000000000000 --- a/drivers/staging/uwb/whc-rc.c +++ /dev/null @@ -1,467 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Wireless Host Controller: Radio Control Interface (WHCI v0.95[2.3]) - * Radio Control command/event transport to the UWB stack - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * Initialize and hook up the Radio Control interface. - * - * For each device probed, creates an 'struct whcrc' which contains - * just the representation of the UWB Radio Controller, and the logic - * for reading notifications and passing them to the UWB Core. - * - * So we initialize all of those, register the UWB Radio Controller - * and setup the notification/event handle to pipe the notifications - * to the UWB management Daemon. - * - * Once uwb_rc_add() is called, the UWB stack takes control, resets - * the radio and readies the device to take commands the UWB - * API/user-space. - * - * Note this driver is just a transport driver; the commands are - * formed at the UWB stack and given to this driver who will deliver - * them to the hw and transfer the replies/notifications back to the - * UWB stack through the UWB daemon (UWBD). - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "uwb.h" -#include "include/whci.h" -#include "include/umc.h" - -#include "uwb-internal.h" - -/** - * Descriptor for an instance of the UWB Radio Control Driver that - * attaches to the URC interface of the WHCI PCI card. - * - * Unless there is a lock specific to the 'data members', all access - * is protected by uwb_rc->mutex. - */ -struct whcrc { - struct umc_dev *umc_dev; - struct uwb_rc *uwb_rc; /* UWB host controller */ - - unsigned long area; - void __iomem *rc_base; - size_t rc_len; - spinlock_t irq_lock; - - void *evt_buf, *cmd_buf; - dma_addr_t evt_dma_buf, cmd_dma_buf; - wait_queue_head_t cmd_wq; - struct work_struct event_work; -}; - -/** - * Execute an UWB RC command on WHCI/RC - * - * @rc: Instance of a Radio Controller that is a whcrc - * @cmd: Buffer containing the RCCB and payload to execute - * @cmd_size: Size of the command buffer. - * - * We copy the command into whcrc->cmd_buf (as it is pretty and - * aligned`and physically contiguous) and then press the right keys in - * the controller's URCCMD register to get it to read it. We might - * have to wait for the cmd_sem to be open to us. - * - * NOTE: rc's mutex has to be locked - */ -static int whcrc_cmd(struct uwb_rc *uwb_rc, - const struct uwb_rccb *cmd, size_t cmd_size) -{ - int result = 0; - struct whcrc *whcrc = uwb_rc->priv; - struct device *dev = &whcrc->umc_dev->dev; - u32 urccmd; - - if (cmd_size >= 4096) - return -EINVAL; - - /* - * If the URC is halted, then the hardware has reset itself. - * Attempt to recover by restarting the device and then return - * an error as it's likely that the current command isn't - * valid for a newly started RC. - */ - if (le_readl(whcrc->rc_base + URCSTS) & URCSTS_HALTED) { - dev_err(dev, "requesting reset of halted radio controller\n"); - uwb_rc_reset_all(uwb_rc); - return -EIO; - } - - result = wait_event_timeout(whcrc->cmd_wq, - !(le_readl(whcrc->rc_base + URCCMD) & URCCMD_ACTIVE), HZ/2); - if (result == 0) { - dev_err(dev, "device is not ready to execute commands\n"); - return -ETIMEDOUT; - } - - memmove(whcrc->cmd_buf, cmd, cmd_size); - le_writeq(whcrc->cmd_dma_buf, whcrc->rc_base + URCCMDADDR); - - spin_lock(&whcrc->irq_lock); - urccmd = le_readl(whcrc->rc_base + URCCMD); - urccmd &= ~(URCCMD_EARV | URCCMD_SIZE_MASK); - le_writel(urccmd | URCCMD_ACTIVE | URCCMD_IWR | cmd_size, - whcrc->rc_base + URCCMD); - spin_unlock(&whcrc->irq_lock); - - return 0; -} - -static int whcrc_reset(struct uwb_rc *rc) -{ - struct whcrc *whcrc = rc->priv; - - return umc_controller_reset(whcrc->umc_dev); -} - -/** - * Reset event reception mechanism and tell hw we are ready to get more - * - * We have read all the events in the event buffer, so we are ready to - * reset it to the beginning. - * - * This is only called during initialization or after an event buffer - * has been retired. This means we can be sure that event processing - * is disabled and it's safe to update the URCEVTADDR register. - * - * There's no need to wait for the event processing to start as the - * URC will not clear URCCMD_ACTIVE until (internal) event buffer - * space is available. - */ -static -void whcrc_enable_events(struct whcrc *whcrc) -{ - u32 urccmd; - - le_writeq(whcrc->evt_dma_buf, whcrc->rc_base + URCEVTADDR); - - spin_lock(&whcrc->irq_lock); - urccmd = le_readl(whcrc->rc_base + URCCMD) & ~URCCMD_ACTIVE; - le_writel(urccmd | URCCMD_EARV, whcrc->rc_base + URCCMD); - spin_unlock(&whcrc->irq_lock); -} - -static void whcrc_event_work(struct work_struct *work) -{ - struct whcrc *whcrc = container_of(work, struct whcrc, event_work); - size_t size; - u64 urcevtaddr; - - urcevtaddr = le_readq(whcrc->rc_base + URCEVTADDR); - size = urcevtaddr & URCEVTADDR_OFFSET_MASK; - - uwb_rc_neh_grok(whcrc->uwb_rc, whcrc->evt_buf, size); - whcrc_enable_events(whcrc); -} - -/** - * Catch interrupts? - * - * We ack inmediately (and expect the hw to do the right thing and - * raise another IRQ if things have changed :) - */ -static -irqreturn_t whcrc_irq_cb(int irq, void *_whcrc) -{ - struct whcrc *whcrc = _whcrc; - struct device *dev = &whcrc->umc_dev->dev; - u32 urcsts; - - urcsts = le_readl(whcrc->rc_base + URCSTS); - if (!(urcsts & URCSTS_INT_MASK)) - return IRQ_NONE; - le_writel(urcsts & URCSTS_INT_MASK, whcrc->rc_base + URCSTS); - - if (urcsts & URCSTS_HSE) { - dev_err(dev, "host system error -- hardware halted\n"); - /* FIXME: do something sensible here */ - goto out; - } - if (urcsts & URCSTS_ER) - schedule_work(&whcrc->event_work); - if (urcsts & URCSTS_RCI) - wake_up_all(&whcrc->cmd_wq); -out: - return IRQ_HANDLED; -} - - -/** - * Initialize a UMC RC interface: map regions, get (shared) IRQ - */ -static -int whcrc_setup_rc_umc(struct whcrc *whcrc) -{ - int result = 0; - struct device *dev = &whcrc->umc_dev->dev; - struct umc_dev *umc_dev = whcrc->umc_dev; - - whcrc->area = umc_dev->resource.start; - whcrc->rc_len = resource_size(&umc_dev->resource); - result = -EBUSY; - if (request_mem_region(whcrc->area, whcrc->rc_len, KBUILD_MODNAME) == NULL) { - dev_err(dev, "can't request URC region (%zu bytes @ 0x%lx): %d\n", - whcrc->rc_len, whcrc->area, result); - goto error_request_region; - } - - whcrc->rc_base = ioremap(whcrc->area, whcrc->rc_len); - if (whcrc->rc_base == NULL) { - dev_err(dev, "can't ioremap registers (%zu bytes @ 0x%lx): %d\n", - whcrc->rc_len, whcrc->area, result); - goto error_ioremap; - } - - result = request_irq(umc_dev->irq, whcrc_irq_cb, IRQF_SHARED, - KBUILD_MODNAME, whcrc); - if (result < 0) { - dev_err(dev, "can't allocate IRQ %d: %d\n", - umc_dev->irq, result); - goto error_request_irq; - } - - result = -ENOMEM; - whcrc->cmd_buf = dma_alloc_coherent(&umc_dev->dev, PAGE_SIZE, - &whcrc->cmd_dma_buf, GFP_KERNEL); - if (whcrc->cmd_buf == NULL) { - dev_err(dev, "Can't allocate cmd transfer buffer\n"); - goto error_cmd_buffer; - } - - whcrc->evt_buf = dma_alloc_coherent(&umc_dev->dev, PAGE_SIZE, - &whcrc->evt_dma_buf, GFP_KERNEL); - if (whcrc->evt_buf == NULL) { - dev_err(dev, "Can't allocate evt transfer buffer\n"); - goto error_evt_buffer; - } - return 0; - -error_evt_buffer: - dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->cmd_buf, - whcrc->cmd_dma_buf); -error_cmd_buffer: - free_irq(umc_dev->irq, whcrc); -error_request_irq: - iounmap(whcrc->rc_base); -error_ioremap: - release_mem_region(whcrc->area, whcrc->rc_len); -error_request_region: - return result; -} - - -/** - * Release RC's UMC resources - */ -static -void whcrc_release_rc_umc(struct whcrc *whcrc) -{ - struct umc_dev *umc_dev = whcrc->umc_dev; - - dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->evt_buf, - whcrc->evt_dma_buf); - dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->cmd_buf, - whcrc->cmd_dma_buf); - free_irq(umc_dev->irq, whcrc); - iounmap(whcrc->rc_base); - release_mem_region(whcrc->area, whcrc->rc_len); -} - - -/** - * whcrc_start_rc - start a WHCI radio controller - * @whcrc: the radio controller to start - * - * Reset the UMC device, start the radio controller, enable events and - * finally enable interrupts. - */ -static int whcrc_start_rc(struct uwb_rc *rc) -{ - struct whcrc *whcrc = rc->priv; - struct device *dev = &whcrc->umc_dev->dev; - - /* Reset the thing */ - le_writel(URCCMD_RESET, whcrc->rc_base + URCCMD); - if (whci_wait_for(dev, whcrc->rc_base + URCCMD, URCCMD_RESET, 0, - 5000, "hardware reset") < 0) - return -EBUSY; - - /* Set the event buffer, start the controller (enable IRQs later) */ - le_writel(0, whcrc->rc_base + URCINTR); - le_writel(URCCMD_RS, whcrc->rc_base + URCCMD); - if (whci_wait_for(dev, whcrc->rc_base + URCSTS, URCSTS_HALTED, 0, - 5000, "radio controller start") < 0) - return -ETIMEDOUT; - whcrc_enable_events(whcrc); - le_writel(URCINTR_EN_ALL, whcrc->rc_base + URCINTR); - return 0; -} - - -/** - * whcrc_stop_rc - stop a WHCI radio controller - * @whcrc: the radio controller to stop - * - * Disable interrupts and cancel any pending event processing work - * before clearing the Run/Stop bit. - */ -static -void whcrc_stop_rc(struct uwb_rc *rc) -{ - struct whcrc *whcrc = rc->priv; - struct umc_dev *umc_dev = whcrc->umc_dev; - - le_writel(0, whcrc->rc_base + URCINTR); - cancel_work_sync(&whcrc->event_work); - - le_writel(0, whcrc->rc_base + URCCMD); - whci_wait_for(&umc_dev->dev, whcrc->rc_base + URCSTS, - URCSTS_HALTED, URCSTS_HALTED, 100, "radio controller stop"); -} - -static void whcrc_init(struct whcrc *whcrc) -{ - spin_lock_init(&whcrc->irq_lock); - init_waitqueue_head(&whcrc->cmd_wq); - INIT_WORK(&whcrc->event_work, whcrc_event_work); -} - -/** - * Initialize the radio controller. - * - * NOTE: we setup whcrc->uwb_rc before calling uwb_rc_add(); in the - * IRQ handler we use that to determine if the hw is ready to - * handle events. Looks like a race condition, but it really is - * not. - */ -static -int whcrc_probe(struct umc_dev *umc_dev) -{ - int result; - struct uwb_rc *uwb_rc; - struct whcrc *whcrc; - struct device *dev = &umc_dev->dev; - - result = -ENOMEM; - uwb_rc = uwb_rc_alloc(); - if (uwb_rc == NULL) { - dev_err(dev, "unable to allocate RC instance\n"); - goto error_rc_alloc; - } - whcrc = kzalloc(sizeof(*whcrc), GFP_KERNEL); - if (whcrc == NULL) { - dev_err(dev, "unable to allocate WHC-RC instance\n"); - goto error_alloc; - } - whcrc_init(whcrc); - whcrc->umc_dev = umc_dev; - - result = whcrc_setup_rc_umc(whcrc); - if (result < 0) { - dev_err(dev, "Can't setup RC UMC interface: %d\n", result); - goto error_setup_rc_umc; - } - whcrc->uwb_rc = uwb_rc; - - uwb_rc->owner = THIS_MODULE; - uwb_rc->cmd = whcrc_cmd; - uwb_rc->reset = whcrc_reset; - uwb_rc->start = whcrc_start_rc; - uwb_rc->stop = whcrc_stop_rc; - - result = uwb_rc_add(uwb_rc, dev, whcrc); - if (result < 0) - goto error_rc_add; - umc_set_drvdata(umc_dev, whcrc); - return 0; - -error_rc_add: - whcrc_release_rc_umc(whcrc); -error_setup_rc_umc: - kfree(whcrc); -error_alloc: - uwb_rc_put(uwb_rc); -error_rc_alloc: - return result; -} - -/** - * Clean up the radio control resources - * - * When we up the command semaphore, everybody possibly held trying to - * execute a command should be granted entry and then they'll see the - * host is quiescing and up it (so it will chain to the next waiter). - * This should not happen (in any case), as we can only remove when - * there are no handles open... - */ -static void whcrc_remove(struct umc_dev *umc_dev) -{ - struct whcrc *whcrc = umc_get_drvdata(umc_dev); - struct uwb_rc *uwb_rc = whcrc->uwb_rc; - - umc_set_drvdata(umc_dev, NULL); - uwb_rc_rm(uwb_rc); - whcrc_release_rc_umc(whcrc); - kfree(whcrc); - uwb_rc_put(uwb_rc); -} - -static int whcrc_pre_reset(struct umc_dev *umc) -{ - struct whcrc *whcrc = umc_get_drvdata(umc); - struct uwb_rc *uwb_rc = whcrc->uwb_rc; - - uwb_rc_pre_reset(uwb_rc); - return 0; -} - -static int whcrc_post_reset(struct umc_dev *umc) -{ - struct whcrc *whcrc = umc_get_drvdata(umc); - struct uwb_rc *uwb_rc = whcrc->uwb_rc; - - return uwb_rc_post_reset(uwb_rc); -} - -/* PCI device ID's that we handle [so it gets loaded] */ -static struct pci_device_id __used whcrc_id_table[] = { - { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) }, - { /* empty last entry */ } -}; -MODULE_DEVICE_TABLE(pci, whcrc_id_table); - -static struct umc_driver whcrc_driver = { - .name = "whc-rc", - .cap_id = UMC_CAP_ID_WHCI_RC, - .probe = whcrc_probe, - .remove = whcrc_remove, - .pre_reset = whcrc_pre_reset, - .post_reset = whcrc_post_reset, -}; - -static int __init whcrc_driver_init(void) -{ - return umc_driver_register(&whcrc_driver); -} -module_init(whcrc_driver_init); - -static void __exit whcrc_driver_exit(void) -{ - umc_driver_unregister(&whcrc_driver); -} -module_exit(whcrc_driver_exit); - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("Wireless Host Controller Radio Control Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/uwb/whci.c b/drivers/staging/uwb/whci.c deleted file mode 100644 index a8832f64d708..000000000000 --- a/drivers/staging/uwb/whci.c +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * WHCI UWB Multi-interface Controller enumerator. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include -#include -#include -#include "include/whci.h" -#include "include/umc.h" - -struct whci_card { - struct pci_dev *pci; - void __iomem *uwbbase; - u8 n_caps; - struct umc_dev *devs[0]; -}; - - -/* Fix faulty HW :( */ -static -u64 whci_capdata_quirks(struct whci_card *card, u64 capdata) -{ - u64 capdata_orig = capdata; - struct pci_dev *pci_dev = card->pci; - if (pci_dev->vendor == PCI_VENDOR_ID_INTEL - && (pci_dev->device == 0x0c3b || pci_dev->device == 0004) - && pci_dev->class == 0x0d1010) { - switch (UWBCAPDATA_TO_CAP_ID(capdata)) { - /* WLP capability has 0x100 bytes of aperture */ - case 0x80: - capdata |= 0x40 << 8; break; - /* WUSB capability has 0x80 bytes of aperture - * and ID is 1 */ - case 0x02: - capdata &= ~0xffff; - capdata |= 0x2001; - break; - } - } - if (capdata_orig != capdata) - dev_warn(&pci_dev->dev, - "PCI v%04x d%04x c%06x#%02x: " - "corrected capdata from %016Lx to %016Lx\n", - pci_dev->vendor, pci_dev->device, pci_dev->class, - (unsigned)UWBCAPDATA_TO_CAP_ID(capdata), - (unsigned long long)capdata_orig, - (unsigned long long)capdata); - return capdata; -} - - -/** - * whci_wait_for - wait for a WHCI register to be set - * - * Polls (for at most @max_ms ms) until '*@reg & @mask == @result'. - */ -int whci_wait_for(struct device *dev, u32 __iomem *reg, u32 mask, u32 result, - unsigned long max_ms, const char *tag) -{ - unsigned t = 0; - u32 val; - for (;;) { - val = le_readl(reg); - if ((val & mask) == result) - break; - if (t >= max_ms) { - dev_err(dev, "%s timed out\n", tag); - return -ETIMEDOUT; - } - msleep(10); - t += 10; - } - return 0; -} -EXPORT_SYMBOL_GPL(whci_wait_for); - - -/* - * NOTE: the capinfo and capdata registers are slightly different - * (size and cap-id fields). So for cap #0, we need to fill - * in. Size comes from the size of the register block - * (statically calculated); cap_id comes from nowhere, we use - * zero, that is reserved, for the radio controller, because - * none was defined at the spec level. - */ -static int whci_add_cap(struct whci_card *card, int n) -{ - struct umc_dev *umc; - u64 capdata; - int bar, err; - - umc = umc_device_create(&card->pci->dev, n); - if (umc == NULL) - return -ENOMEM; - - capdata = le_readq(card->uwbbase + UWBCAPDATA(n)); - - bar = UWBCAPDATA_TO_BAR(capdata) << 1; - - capdata = whci_capdata_quirks(card, capdata); - /* Capability 0 is the radio controller. It's size is 32 - * bytes (WHCI0.95[2.3, T2-9]). */ - umc->version = UWBCAPDATA_TO_VERSION(capdata); - umc->cap_id = n == 0 ? 0 : UWBCAPDATA_TO_CAP_ID(capdata); - umc->bar = bar; - umc->resource.start = pci_resource_start(card->pci, bar) - + UWBCAPDATA_TO_OFFSET(capdata); - umc->resource.end = umc->resource.start - + (n == 0 ? 0x20 : UWBCAPDATA_TO_SIZE(capdata)) - 1; - umc->resource.name = dev_name(&umc->dev); - umc->resource.flags = card->pci->resource[bar].flags; - umc->resource.parent = &card->pci->resource[bar]; - umc->irq = card->pci->irq; - - err = umc_device_register(umc); - if (err < 0) - goto error; - card->devs[n] = umc; - return 0; - -error: - kfree(umc); - return err; -} - -static void whci_del_cap(struct whci_card *card, int n) -{ - struct umc_dev *umc = card->devs[n]; - - umc_device_unregister(umc); -} - -static int whci_n_caps(struct pci_dev *pci) -{ - void __iomem *uwbbase; - u64 capinfo; - - uwbbase = pci_iomap(pci, 0, 8); - if (!uwbbase) - return -ENOMEM; - capinfo = le_readq(uwbbase + UWBCAPINFO); - pci_iounmap(pci, uwbbase); - - return UWBCAPINFO_TO_N_CAPS(capinfo); -} - -static int whci_probe(struct pci_dev *pci, const struct pci_device_id *id) -{ - struct whci_card *card; - int err, n_caps, n; - - err = pci_enable_device(pci); - if (err < 0) - goto error; - pci_enable_msi(pci); - pci_set_master(pci); - err = -ENXIO; - if (!pci_set_dma_mask(pci, DMA_BIT_MASK(64))) - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64)); - else if (!pci_set_dma_mask(pci, DMA_BIT_MASK(32))) - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)); - else - goto error_dma; - - err = n_caps = whci_n_caps(pci); - if (n_caps < 0) - goto error_ncaps; - - err = -ENOMEM; - card = kzalloc(sizeof(struct whci_card) - + sizeof(struct umc_dev *) * (n_caps + 1), - GFP_KERNEL); - if (card == NULL) - goto error_kzalloc; - card->pci = pci; - card->n_caps = n_caps; - - err = -EBUSY; - if (!request_mem_region(pci_resource_start(pci, 0), - UWBCAPDATA_SIZE(card->n_caps), - "whci (capability data)")) - goto error_request_memregion; - err = -ENOMEM; - card->uwbbase = pci_iomap(pci, 0, UWBCAPDATA_SIZE(card->n_caps)); - if (!card->uwbbase) - goto error_iomap; - - /* Add each capability. */ - for (n = 0; n <= card->n_caps; n++) { - err = whci_add_cap(card, n); - if (err < 0 && n == 0) { - dev_err(&pci->dev, "cannot bind UWB radio controller:" - " %d\n", err); - goto error_bind; - } - if (err < 0) - dev_warn(&pci->dev, "warning: cannot bind capability " - "#%u: %d\n", n, err); - } - pci_set_drvdata(pci, card); - return 0; - -error_bind: - pci_iounmap(pci, card->uwbbase); -error_iomap: - release_mem_region(pci_resource_start(pci, 0), UWBCAPDATA_SIZE(card->n_caps)); -error_request_memregion: - kfree(card); -error_kzalloc: -error_ncaps: -error_dma: - pci_disable_msi(pci); - pci_disable_device(pci); -error: - return err; -} - -static void whci_remove(struct pci_dev *pci) -{ - struct whci_card *card = pci_get_drvdata(pci); - int n; - - pci_set_drvdata(pci, NULL); - /* Unregister each capability in reverse (so the master device - * is unregistered last). */ - for (n = card->n_caps; n >= 0 ; n--) - whci_del_cap(card, n); - pci_iounmap(pci, card->uwbbase); - release_mem_region(pci_resource_start(pci, 0), UWBCAPDATA_SIZE(card->n_caps)); - kfree(card); - pci_disable_msi(pci); - pci_disable_device(pci); -} - -static struct pci_device_id whci_id_table[] = { - { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) }, - { 0 }, -}; -MODULE_DEVICE_TABLE(pci, whci_id_table); - - -static struct pci_driver whci_driver = { - .name = "whci", - .id_table = whci_id_table, - .probe = whci_probe, - .remove = whci_remove, -}; - -module_pci_driver(whci_driver); -MODULE_DESCRIPTION("WHCI UWB Multi-interface Controller enumerator"); -MODULE_AUTHOR("Cambridge Silicon Radio Ltd."); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/wusbcore/Documentation/wusb-cbaf b/drivers/staging/wusbcore/Documentation/wusb-cbaf deleted file mode 100644 index 8b3d43efce90..000000000000 --- a/drivers/staging/wusbcore/Documentation/wusb-cbaf +++ /dev/null @@ -1,130 +0,0 @@ -#! /bin/bash -# - -set -e - -progname=$(basename $0) -function help -{ - cat < $dev/wusb_chid - echo I: started host $(basename $dev) >&2 - done - ;; - stop) - for dev in ${2:-$hdevs} - do - echo 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > $dev/wusb_chid - echo I: stopped host $(basename $dev) >&2 - done - ;; - set-chid) - shift - for dev in ${2:-$devs}; do - echo "${4:-$host_name}" > $dev/wusb_host_name - echo "${3:-$host_band_group}" > $dev/wusb_host_band_groups - echo ${2:-$host_CHID} > $dev/wusb_chid - done - ;; - get-cdid) - for dev in ${2:-$devs} - do - cat $dev/wusb_cdid - done - ;; - set-cc) - for dev in ${2:-$devs}; do - shift - CDID="$(head --bytes=16 /dev/urandom | od -tx1 -An)" - CK="$(head --bytes=16 /dev/urandom | od -tx1 -An)" - echo "$CDID" > $dev/wusb_cdid - echo "$CK" > $dev/wusb_ck - - echo I: CC set >&2 - echo "CHID: $(cat $dev/wusb_chid)" - echo "CDID:$CDID" - echo "CK: $CK" - done - ;; - help|h|--help|-h) - help - ;; - *) - echo "E: Unknown usage" 1>&2 - help 1>&2 - result=1 -esac -exit $result diff --git a/drivers/staging/wusbcore/Documentation/wusb-design-overview.rst b/drivers/staging/wusbcore/Documentation/wusb-design-overview.rst deleted file mode 100644 index dc5e21609bb5..000000000000 --- a/drivers/staging/wusbcore/Documentation/wusb-design-overview.rst +++ /dev/null @@ -1,457 +0,0 @@ -================================ -Linux UWB + Wireless USB + WiNET -================================ - - Copyright (C) 2005-2006 Intel Corporation - - Inaky Perez-Gonzalez - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License version - 2 as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - - -Please visit http://bughost.org/thewiki/Design-overview.txt-1.8 for -updated content. - - * Design-overview.txt-1.8 - -This code implements a Ultra Wide Band stack for Linux, as well as -drivers for the USB based UWB radio controllers defined in the -Wireless USB 1.0 specification (including Wireless USB host controller -and an Intel WiNET controller). - -.. Contents - 1. Introduction - 1. HWA: Host Wire adapters, your Wireless USB dongle - - 2. DWA: Device Wired Adaptor, a Wireless USB hub for wired - devices - 3. WHCI: Wireless Host Controller Interface, the PCI WUSB host - adapter - 2. The UWB stack - 1. Devices and hosts: the basic structure - - 2. Host Controller life cycle - - 3. On the air: beacons and enumerating the radio neighborhood - - 4. Device lists - 5. Bandwidth allocation - - 3. Wireless USB Host Controller drivers - - 4. Glossary - - -Introduction -============ - -UWB is a wide-band communication protocol that is to serve also as the -low-level protocol for others (much like TCP sits on IP). Currently -these others are Wireless USB and TCP/IP, but seems Bluetooth and -Firewire/1394 are coming along. - -UWB uses a band from roughly 3 to 10 GHz, transmitting at a max of -~-41dB (or 0.074 uW/MHz--geography specific data is still being -negotiated w/ regulators, so watch for changes). That band is divided in -a bunch of ~1.5 GHz wide channels (or band groups) composed of three -subbands/subchannels (528 MHz each). Each channel is independent of each -other, so you could consider them different "busses". Initially this -driver considers them all a single one. - -Radio time is divided in 65536 us long /superframes/, each one divided -in 256 256us long /MASs/ (Media Allocation Slots), which are the basic -time/media allocation units for transferring data. At the beginning of -each superframe there is a Beacon Period (BP), where every device -transmit its beacon on a single MAS. The length of the BP depends on how -many devices are present and the length of their beacons. - -Devices have a MAC (fixed, 48 bit address) and a device (changeable, 16 -bit address) and send periodic beacons to advertise themselves and pass -info on what they are and do. They advertise their capabilities and a -bunch of other stuff. - -The different logical parts of this driver are: - - * - - *UWB*: the Ultra-Wide-Band stack -- manages the radio and - associated spectrum to allow for devices sharing it. Allows to - control bandwidth assignment, beaconing, scanning, etc - - * - - *WUSB*: the layer that sits on top of UWB to provide Wireless USB. - The Wireless USB spec defines means to control a UWB radio and to - do the actual WUSB. - - -HWA: Host Wire adapters, your Wireless USB dongle -------------------------------------------------- - -WUSB also defines a device called a Host Wire Adaptor (HWA), which in -mere terms is a USB dongle that enables your PC to have UWB and Wireless -USB. The Wireless USB Host Controller in a HWA looks to the host like a -[Wireless] USB controller connected via USB (!) - -The HWA itself is broken in two or three main interfaces: - - * - - *RC*: Radio control -- this implements an interface to the - Ultra-Wide-Band radio controller. The driver for this implements a - USB-based UWB Radio Controller to the UWB stack. - - * - - *HC*: the wireless USB host controller. It looks like a USB host - whose root port is the radio and the WUSB devices connect to it. - To the system it looks like a separate USB host. The driver (will) - implement a USB host controller (similar to UHCI, OHCI or EHCI) - for which the root hub is the radio...To reiterate: it is a USB - controller that is connected via USB instead of PCI. - - * - - *WINET*: some HW provide a WiNET interface (IP over UWB). This - package provides a driver for it (it looks like a network - interface, winetX). The driver detects when there is a link up for - their type and kick into gear. - - -DWA: Device Wired Adaptor, a Wireless USB hub for wired devices ---------------------------------------------------------------- - -These are the complement to HWAs. They are a USB host for connecting -wired devices, but it is connected to your PC connected via Wireless -USB. To the system it looks like yet another USB host. To the untrained -eye, it looks like a hub that connects upstream wirelessly. - -We still offer no support for this; however, it should share a lot of -code with the HWA-RC driver; there is a bunch of factorization work that -has been done to support that in upcoming releases. - - -WHCI: Wireless Host Controller Interface, the PCI WUSB host adapter -------------------------------------------------------------------- - -This is your usual PCI device that implements WHCI. Similar in concept -to EHCI, it allows your wireless USB devices (including DWAs) to connect -to your host via a PCI interface. As in the case of the HWA, it has a -Radio Control interface and the WUSB Host Controller interface per se. - -There is still no driver support for this, but will be in upcoming -releases. - - -The UWB stack -============= - -The main mission of the UWB stack is to keep a tally of which devices -are in radio proximity to allow drivers to connect to them. As well, it -provides an API for controlling the local radio controllers (RCs from -now on), such as to start/stop beaconing, scan, allocate bandwidth, etc. - - -Devices and hosts: the basic structure --------------------------------------- - -The main building block here is the UWB device (struct uwb_dev). For -each device that pops up in radio presence (ie: the UWB host receives a -beacon from it) you get a struct uwb_dev that will show up in -/sys/bus/uwb/devices. - -For each RC that is detected, a new struct uwb_rc and struct uwb_dev are -created. An entry is also created in /sys/class/uwb_rc for each RC. - -Each RC driver is implemented by a separate driver that plugs into the -interface that the UWB stack provides through a struct uwb_rc_ops. The -spec creators have been nice enough to make the message format the same -for HWA and WHCI RCs, so the driver is really a very thin transport that -moves the requests from the UWB API to the device [/uwb_rc_ops->cmd()/] -and sends the replies and notifications back to the API -[/uwb_rc_neh_grok()/]. Notifications are handled to the UWB daemon, that -is chartered, among other things, to keep the tab of how the UWB radio -neighborhood looks, creating and destroying devices as they show up or -disappear. - -Command execution is very simple: a command block is sent and a event -block or reply is expected back. For sending/receiving command/events, a -handle called /neh/ (Notification/Event Handle) is opened with -/uwb_rc_neh_open()/. - -The HWA-RC (USB dongle) driver (drivers/uwb/hwa-rc.c) does this job for -the USB connected HWA. Eventually, drivers/whci-rc.c will do the same -for the PCI connected WHCI controller. - - -Host Controller life cycle --------------------------- - -So let's say we connect a dongle to the system: it is detected and -firmware uploaded if needed [for Intel's i1480 -/drivers/uwb/ptc/usb.c:ptc_usb_probe()/] and then it is reenumerated. -Now we have a real HWA device connected and -/drivers/uwb/hwa-rc.c:hwarc_probe()/ picks it up, that will set up the -Wire-Adaptor environment and then suck it into the UWB stack's vision of -the world [/drivers/uwb/lc-rc.c:uwb_rc_add()/]. - - * - - [*] The stack should put a new RC to scan for devices - [/uwb_rc_scan()/] so it finds what's available around and tries to - connect to them, but this is policy stuff and should be driven - from user space. As of now, the operator is expected to do it - manually; see the release notes for documentation on the procedure. - -When a dongle is disconnected, /drivers/uwb/hwa-rc.c:hwarc_disconnect()/ -takes time of tearing everything down safely (or not...). - - -On the air: beacons and enumerating the radio neighborhood ----------------------------------------------------------- - -So assuming we have devices and we have agreed for a channel to connect -on (let's say 9), we put the new RC to beacon: - - * - - $ echo 9 0 > /sys/class/uwb_rc/uwb0/beacon - -Now it is visible. If there were other devices in the same radio channel -and beacon group (that's what the zero is for), the dongle's radio -control interface will send beacon notifications on its -notification/event endpoint (NEEP). The beacon notifications are part of -the event stream that is funneled into the API with -/drivers/uwb/neh.c:uwb_rc_neh_grok()/ and delivered to the UWBD, the UWB -daemon through a notification list. - -UWBD wakes up and scans the event list; finds a beacon and adds it to -the BEACON CACHE (/uwb_beca/). If he receives a number of beacons from -the same device, he considers it to be 'onair' and creates a new device -[/drivers/uwb/lc-dev.c:uwbd_dev_onair()/]. Similarly, when no beacons -are received in some time, the device is considered gone and wiped out -[uwbd calls periodically /uwb/beacon.c:uwb_beca_purge()/ that will purge -the beacon cache of dead devices]. - - -Device lists ------------- - -All UWB devices are kept in the list of the struct bus_type uwb_bus_type. - - -Bandwidth allocation --------------------- - -The UWB stack maintains a local copy of DRP availability through -processing of incoming *DRP Availability Change* notifications. This -local copy is currently used to present the current bandwidth -availability to the user through the sysfs file -/sys/class/uwb_rc/uwbx/bw_avail. In the future the bandwidth -availability information will be used by the bandwidth reservation -routines. - -The bandwidth reservation routines are in progress and are thus not -present in the current release. When completed they will enable a user -to initiate DRP reservation requests through interaction with sysfs. DRP -reservation requests from remote UWB devices will also be handled. The -bandwidth management done by the UWB stack will include callbacks to the -higher layers will enable the higher layers to use the reservations upon -completion. [Note: The bandwidth reservation work is in progress and -subject to change.] - - -Wireless USB Host Controller drivers -==================================== - -*WARNING* This section needs a lot of work! - -As explained above, there are three different types of HCs in the WUSB -world: HWA-HC, DWA-HC and WHCI-HC. - -HWA-HC and DWA-HC share that they are Wire-Adapters (USB or WUSB -connected controllers), and their transfer management system is almost -identical. So is their notification delivery system. - -HWA-HC and WHCI-HC share that they are both WUSB host controllers, so -they have to deal with WUSB device life cycle and maintenance, wireless -root-hub - -HWA exposes a Host Controller interface (HWA-HC 0xe0/02/02). This has -three endpoints (Notifications, Data Transfer In and Data Transfer -Out--known as NEP, DTI and DTO in the code). - -We reserve UWB bandwidth for our Wireless USB Cluster, create a Cluster -ID and tell the HC to use all that. Then we start it. This means the HC -starts sending MMCs. - - * - - The MMCs are blocks of data defined somewhere in the WUSB1.0 spec - that define a stream in the UWB channel time allocated for sending - WUSB IEs (host to device commands/notifications) and Device - Notifications (device initiated to host). Each host defines a - unique Wireless USB cluster through MMCs. Devices can connect to a - single cluster at the time. The IEs are Information Elements, and - among them are the bandwidth allocations that tell each device - when can they transmit or receive. - -Now it all depends on external stimuli. - -New device connection ---------------------- - -A new device pops up, it scans the radio looking for MMCs that give out -the existence of Wireless USB channels. Once one (or more) are found, -selects which one to connect to. Sends a /DN_Connect/ (device -notification connect) during the DNTS (Device Notification Time -Slot--announced in the MMCs - -HC picks the /DN_Connect/ out (nep module sends to notif.c for delivery -into /devconnect/). This process starts the authentication process for -the device. First we allocate a /fake port/ and assign an -unauthenticated address (128 to 255--what we really do is -0x80 | fake_port_idx). We fiddle with the fake port status and /hub_wq/ -sees a new connection, so he moves on to enable the fake port with a reset. - -So now we are in the reset path -- we know we have a non-yet enumerated -device with an unauthorized address; we ask user space to authenticate -(FIXME: not yet done, similar to bluetooth pairing), then we do the key -exchange (FIXME: not yet done) and issue a /set address 0/ to bring the -device to the default state. Device is authenticated. - -From here, the USB stack takes control through the usb_hcd ops. hub_wq -has seen the port status changes, as we have been toggling them. It will -start enumerating and doing transfers through usb_hcd->urb_enqueue() to -read descriptors and move our data. - -Device life cycle and keep alives ---------------------------------- - -Every time there is a successful transfer to/from a device, we update a -per-device activity timestamp. If not, every now and then we check and -if the activity timestamp gets old, we ping the device by sending it a -Keep Alive IE; it responds with a /DN_Alive/ pong during the DNTS (this -arrives to us as a notification through -devconnect.c:wusb_handle_dn_alive(). If a device times out, we -disconnect it from the system (cleaning up internal information and -toggling the bits in the fake hub port, which kicks hub_wq into removing -the rest of the stuff). - -This is done through devconnect:__wusb_check_devs(), which will scan the -device list looking for whom needs refreshing. - -If the device wants to disconnect, it will either die (ugly) or send a -/DN_Disconnect/ that will prompt a disconnection from the system. - -Sending and receiving data --------------------------- - -Data is sent and received through /Remote Pipes/ (rpipes). An rpipe is -/aimed/ at an endpoint in a WUSB device. This is the same for HWAs and -DWAs. - -Each HC has a number of rpipes and buffers that can be assigned to them; -when doing a data transfer (xfer), first the rpipe has to be aimed and -prepared (buffers assigned), then we can start queueing requests for -data in or out. - -Data buffers have to be segmented out before sending--so we send first a -header (segment request) and then if there is any data, a data buffer -immediately after to the DTI interface (yep, even the request). If our -buffer is bigger than the max segment size, then we just do multiple -requests. - -[This sucks, because doing USB scatter gatter in Linux is resource -intensive, if any...not that the current approach is not. It just has to -be cleaned up a lot :)]. - -If reading, we don't send data buffers, just the segment headers saying -we want to read segments. - -When the xfer is executed, we receive a notification that says data is -ready in the DTI endpoint (handled through -xfer.c:wa_handle_notif_xfer()). In there we read from the DTI endpoint a -descriptor that gives us the status of the transfer, its identification -(given when we issued it) and the segment number. If it was a data read, -we issue another URB to read into the destination buffer the chunk of -data coming out of the remote endpoint. Done, wait for the next guy. The -callbacks for the URBs issued from here are the ones that will declare -the xfer complete at some point and call its callback. - -Seems simple, but the implementation is not trivial. - - * - - *WARNING* Old!! - -The main xfer descriptor, wa_xfer (equivalent to a URB) contains an -array of segments, tallys on segments and buffers and callback -information. Buried in there is a lot of URBs for executing the segments -and buffer transfers. - -For OUT xfers, there is an array of segments, one URB for each, another -one of buffer URB. When submitting, we submit URBs for segment request -1, buffer 1, segment 2, buffer 2...etc. Then we wait on the DTI for xfer -result data; when all the segments are complete, we call the callback to -finalize the transfer. - -For IN xfers, we only issue URBs for the segments we want to read and -then wait for the xfer result data. - -URB mapping into xfers -^^^^^^^^^^^^^^^^^^^^^^ - -This is done by hwahc_op_urb_[en|de]queue(). In enqueue() we aim an -rpipe to the endpoint where we have to transmit, create a transfer -context (wa_xfer) and submit it. When the xfer is done, our callback is -called and we assign the status bits and release the xfer resources. - -In dequeue() we are basically cancelling/aborting the transfer. We issue -a xfer abort request to the HC, cancel all the URBs we had submitted -and not yet done and when all that is done, the xfer callback will be -called--this will call the URB callback. - - -Glossary -======== - -*DWA* -- Device Wire Adapter - -USB host, wired for downstream devices, upstream connects wirelessly -with Wireless USB. - -*EVENT* -- Response to a command on the NEEP - -*HWA* -- Host Wire Adapter / USB dongle for UWB and Wireless USB - -*NEH* -- Notification/Event Handle - -Handle/file descriptor for receiving notifications or events. The WA -code requires you to get one of this to listen for notifications or -events on the NEEP. - -*NEEP* -- Notification/Event EndPoint - -Stuff related to the management of the first endpoint of a HWA USB -dongle that is used to deliver an stream of events and notifications to -the host. - -*NOTIFICATION* -- Message coming in the NEEP as response to something. - -*RC* -- Radio Control - -Design-overview.txt-1.8 (last edited 2006-11-04 12:22:24 by -InakyPerezGonzalez) diff --git a/drivers/staging/wusbcore/Kconfig b/drivers/staging/wusbcore/Kconfig deleted file mode 100644 index a559d023b508..000000000000 --- a/drivers/staging/wusbcore/Kconfig +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Wireless USB Core configuration -# -config USB_WUSB - tristate "Enable Wireless USB extensions" - depends on UWB && USB - select CRYPTO - select CRYPTO_AES - select CRYPTO_CCM - help - Enable the host-side support for Wireless USB. - - To compile this support select Y (built in). It is safe to - select even if you don't have the hardware. - -config USB_WUSB_CBAF - tristate "Support WUSB Cable Based Association (CBA)" - depends on USB - help - Some WUSB devices support Cable Based Association. It's used to - enable the secure communication between the host and the - device. - - Enable this option if your WUSB device must to be connected - via wired USB before establishing a wireless link. - - It is safe to select even if you don't have a compatible - hardware. - -config USB_WUSB_CBAF_DEBUG - bool "Enable CBA debug messages" - depends on USB_WUSB_CBAF - help - Say Y here if you want the CBA to produce a bunch of debug messages - to the system log. Select this if you are having a problem with - CBA support and want to see more of what is going on. - -source "drivers/staging/wusbcore/host/Kconfig" diff --git a/drivers/staging/wusbcore/Makefile b/drivers/staging/wusbcore/Makefile deleted file mode 100644 index b47b874268ac..000000000000 --- a/drivers/staging/wusbcore/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -ccflags-$(CONFIG_USB_WUSB_CBAF_DEBUG) := -DDEBUG - -obj-$(CONFIG_USB_WUSB) += wusbcore.o -obj-$(CONFIG_USB_HWA_HCD) += wusb-wa.o -obj-$(CONFIG_USB_WUSB_CBAF) += wusb-cbaf.o - - -wusbcore-y := \ - crypto.o \ - devconnect.o \ - dev-sysfs.o \ - mmc.o \ - pal.o \ - rh.o \ - reservation.o \ - security.o \ - wusbhc.o - -wusb-cbaf-y := cbaf.o - -wusb-wa-y := \ - wa-hc.o \ - wa-nep.o \ - wa-rpipe.o \ - wa-xfer.o - -obj-y += host/ diff --git a/drivers/staging/wusbcore/TODO b/drivers/staging/wusbcore/TODO deleted file mode 100644 index abae57000534..000000000000 --- a/drivers/staging/wusbcore/TODO +++ /dev/null @@ -1,8 +0,0 @@ -TODO: Remove in late 2019 unless there are users - -There seems to not be any real wireless USB devices anywhere in the wild -anymore. It turned out to be a failed technology :( - -This will be removed from the tree if no one objects. - -Greg Kroah-Hartman diff --git a/drivers/staging/wusbcore/cbaf.c b/drivers/staging/wusbcore/cbaf.c deleted file mode 100644 index 57062eaf7558..000000000000 --- a/drivers/staging/wusbcore/cbaf.c +++ /dev/null @@ -1,645 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB - Cable Based Association - * - * - * Copyright (C) 2006 Intel Corporation - * Inaky Perez-Gonzalez - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - * - * WUSB devices have to be paired (associated in WUSB lingo) so - * that they can connect to the system. - * - * One way of pairing is using CBA-Cable Based Association. First - * time you plug the device with a cable, association is done between - * host and device and subsequent times, you can connect wirelessly - * without having to associate again. That's the idea. - * - * This driver does nothing Earth shattering. It just provides an - * interface to chat with the wire-connected device so we can get a - * CDID (device ID) that might have been previously associated to a - * CHID (host ID) and to set up a new triplet - * (connection context), with the CK being the secret, or connection - * key. This is the pairing data. - * - * When a device with the CBA capability connects, the probe routine - * just creates a bunch of sysfs files that a user space enumeration - * manager uses to allow it to connect wirelessly to the system or not. - * - * The process goes like this: - * - * 1. Device plugs, cbaf is loaded, notifications happen. - * - * 2. The connection manager (CM) sees a device with CBAF capability - * (the wusb_chid etc. files in /sys/devices/blah/OURDEVICE). - * - * 3. The CM writes the host name, supported band groups, and the CHID - * (host ID) into the wusb_host_name, wusb_host_band_groups and - * wusb_chid files. These get sent to the device and the CDID (if - * any) for this host is requested. - * - * 4. The CM can verify that the device's supported band groups - * (wusb_device_band_groups) are compatible with the host. - * - * 5. The CM reads the wusb_cdid file. - * - * 6. The CM looks up its database - * - * 6.1 If it has a matching CHID,CDID entry, the device has been - * authorized before (paired) and nothing further needs to be - * done. - * - * 6.2 If the CDID is zero (or the CM doesn't find a matching CDID in - * its database), the device is assumed to be not known. The CM - * may associate the host with device by: writing a randomly - * generated CDID to wusb_cdid and then a random CK to wusb_ck - * (this uploads the new CC to the device). - * - * CMD may choose to prompt the user before associating with a new - * device. - * - * 7. Device is unplugged. - * - * When the device tries to connect wirelessly, it will present its - * CDID to the WUSB host controller. The CM will query the - * database. If the CHID/CDID pair found, it will (with a 4-way - * handshake) challenge the device to demonstrate it has the CK secret - * key (from our database) without actually exchanging it. Once - * satisfied, crypto keys are derived from the CK, the device is - * connected and all communication is encrypted. - * - * References: - * [WUSB-AM] Association Models Supplement to the Certified Wireless - * Universal Serial Bus Specification, version 1.0. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "../uwb/uwb.h" -#include "include/wusb.h" -#include "include/association.h" - -#define CBA_NAME_LEN 0x40 /* [WUSB-AM] table 4-7 */ - -/* An instance of a Cable-Based-Association-Framework device */ -struct cbaf { - struct usb_device *usb_dev; - struct usb_interface *usb_iface; - void *buffer; - size_t buffer_size; - - struct wusb_ckhdid chid; - char host_name[CBA_NAME_LEN]; - u16 host_band_groups; - - struct wusb_ckhdid cdid; - char device_name[CBA_NAME_LEN]; - u16 device_band_groups; - - struct wusb_ckhdid ck; -}; - -/* - * Verify that a CBAF USB-interface has what we need - * - * According to [WUSB-AM], CBA devices should provide at least two - * interfaces: - * - RETRIEVE_HOST_INFO - * - ASSOCIATE - * - * If the device doesn't provide these interfaces, we do not know how - * to deal with it. - */ -static int cbaf_check(struct cbaf *cbaf) -{ - int result; - struct device *dev = &cbaf->usb_iface->dev; - struct wusb_cbaf_assoc_info *assoc_info; - struct wusb_cbaf_assoc_request *assoc_request; - size_t assoc_size; - void *itr, *top; - int ar_rhi = 0, ar_assoc = 0; - - result = usb_control_msg( - cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0), - CBAF_REQ_GET_ASSOCIATION_INFORMATION, - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, - cbaf->buffer, cbaf->buffer_size, USB_CTRL_GET_TIMEOUT); - if (result < 0) { - dev_err(dev, "Cannot get available association types: %d\n", - result); - return result; - } - - assoc_info = cbaf->buffer; - if (result < sizeof(*assoc_info)) { - dev_err(dev, "Not enough data to decode association info " - "header (%zu vs %zu bytes required)\n", - (size_t)result, sizeof(*assoc_info)); - return result; - } - - assoc_size = le16_to_cpu(assoc_info->Length); - if (result < assoc_size) { - dev_err(dev, "Not enough data to decode association info " - "(%zu vs %zu bytes required)\n", - (size_t)assoc_size, sizeof(*assoc_info)); - return result; - } - /* - * From now on, we just verify, but won't error out unless we - * don't find the AR_TYPE_WUSB_{RETRIEVE_HOST_INFO,ASSOCIATE} - * types. - */ - itr = cbaf->buffer + sizeof(*assoc_info); - top = cbaf->buffer + assoc_size; - dev_dbg(dev, "Found %u association requests (%zu bytes)\n", - assoc_info->NumAssociationRequests, assoc_size); - - while (itr < top) { - u16 ar_type, ar_subtype; - u32 ar_size; - const char *ar_name; - - assoc_request = itr; - - if (top - itr < sizeof(*assoc_request)) { - dev_err(dev, "Not enough data to decode association " - "request (%zu vs %zu bytes needed)\n", - top - itr, sizeof(*assoc_request)); - break; - } - - ar_type = le16_to_cpu(assoc_request->AssociationTypeId); - ar_subtype = le16_to_cpu(assoc_request->AssociationSubTypeId); - ar_size = le32_to_cpu(assoc_request->AssociationTypeInfoSize); - ar_name = "unknown"; - - switch (ar_type) { - case AR_TYPE_WUSB: - /* Verify we have what is mandated by [WUSB-AM]. */ - switch (ar_subtype) { - case AR_TYPE_WUSB_RETRIEVE_HOST_INFO: - ar_name = "RETRIEVE_HOST_INFO"; - ar_rhi = 1; - break; - case AR_TYPE_WUSB_ASSOCIATE: - /* send assoc data */ - ar_name = "ASSOCIATE"; - ar_assoc = 1; - break; - } - break; - } - - dev_dbg(dev, "Association request #%02u: 0x%04x/%04x " - "(%zu bytes): %s\n", - assoc_request->AssociationDataIndex, ar_type, - ar_subtype, (size_t)ar_size, ar_name); - - itr += sizeof(*assoc_request); - } - - if (!ar_rhi) { - dev_err(dev, "Missing RETRIEVE_HOST_INFO association " - "request\n"); - return -EINVAL; - } - if (!ar_assoc) { - dev_err(dev, "Missing ASSOCIATE association request\n"); - return -EINVAL; - } - - return 0; -} - -static const struct wusb_cbaf_host_info cbaf_host_info_defaults = { - .AssociationTypeId_hdr = WUSB_AR_AssociationTypeId, - .AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB), - .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId, - .AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_RETRIEVE_HOST_INFO), - .CHID_hdr = WUSB_AR_CHID, - .LangID_hdr = WUSB_AR_LangID, - .HostFriendlyName_hdr = WUSB_AR_HostFriendlyName, -}; - -/* Send WUSB host information (CHID and name) to a CBAF device */ -static int cbaf_send_host_info(struct cbaf *cbaf) -{ - struct wusb_cbaf_host_info *hi; - size_t name_len; - size_t hi_size; - - hi = cbaf->buffer; - memset(hi, 0, sizeof(*hi)); - *hi = cbaf_host_info_defaults; - hi->CHID = cbaf->chid; - hi->LangID = 0; /* FIXME: I guess... */ - strlcpy(hi->HostFriendlyName, cbaf->host_name, CBA_NAME_LEN); - name_len = strlen(cbaf->host_name); - hi->HostFriendlyName_hdr.len = cpu_to_le16(name_len); - hi_size = sizeof(*hi) + name_len; - - return usb_control_msg(cbaf->usb_dev, - usb_sndctrlpipe(cbaf->usb_dev, 0), - CBAF_REQ_SET_ASSOCIATION_RESPONSE, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0x0101, - cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, - hi, hi_size, USB_CTRL_SET_TIMEOUT); -} - -/* - * Get device's information (CDID) associated to CHID - * - * The device will return it's information (CDID, name, bandgroups) - * associated to the CHID we have set before, or 0 CDID and default - * name and bandgroup if no CHID set or unknown. - */ -static int cbaf_cdid_get(struct cbaf *cbaf) -{ - int result; - struct device *dev = &cbaf->usb_iface->dev; - struct wusb_cbaf_device_info *di; - size_t needed; - - di = cbaf->buffer; - result = usb_control_msg( - cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0), - CBAF_REQ_GET_ASSOCIATION_REQUEST, - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0x0200, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, - di, cbaf->buffer_size, USB_CTRL_GET_TIMEOUT); - if (result < 0) { - dev_err(dev, "Cannot request device information: %d\n", - result); - return result; - } - - needed = result < sizeof(*di) ? sizeof(*di) : le32_to_cpu(di->Length); - if (result < needed) { - dev_err(dev, "Not enough data in DEVICE_INFO reply (%zu vs " - "%zu bytes needed)\n", (size_t)result, needed); - return -ENOENT; - } - - strlcpy(cbaf->device_name, di->DeviceFriendlyName, CBA_NAME_LEN); - cbaf->cdid = di->CDID; - cbaf->device_band_groups = le16_to_cpu(di->BandGroups); - - return 0; -} - -static ssize_t cbaf_wusb_chid_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - return sprintf(buf, "%16ph\n", cbaf->chid.data); -} - -static ssize_t cbaf_wusb_chid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - ssize_t result; - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - result = sscanf(buf, - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx", - &cbaf->chid.data[0] , &cbaf->chid.data[1], - &cbaf->chid.data[2] , &cbaf->chid.data[3], - &cbaf->chid.data[4] , &cbaf->chid.data[5], - &cbaf->chid.data[6] , &cbaf->chid.data[7], - &cbaf->chid.data[8] , &cbaf->chid.data[9], - &cbaf->chid.data[10], &cbaf->chid.data[11], - &cbaf->chid.data[12], &cbaf->chid.data[13], - &cbaf->chid.data[14], &cbaf->chid.data[15]); - - if (result != 16) - return -EINVAL; - - result = cbaf_send_host_info(cbaf); - if (result < 0) - return result; - result = cbaf_cdid_get(cbaf); - if (result < 0) - return result; - return size; -} -static DEVICE_ATTR(wusb_chid, 0600, cbaf_wusb_chid_show, cbaf_wusb_chid_store); - -static ssize_t cbaf_wusb_host_name_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - return scnprintf(buf, PAGE_SIZE, "%s\n", cbaf->host_name); -} - -static ssize_t cbaf_wusb_host_name_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - ssize_t result; - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - result = sscanf(buf, "%63s", cbaf->host_name); - if (result != 1) - return -EINVAL; - - return size; -} -static DEVICE_ATTR(wusb_host_name, 0600, cbaf_wusb_host_name_show, - cbaf_wusb_host_name_store); - -static ssize_t cbaf_wusb_host_band_groups_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - return scnprintf(buf, PAGE_SIZE, "0x%04x\n", cbaf->host_band_groups); -} - -static ssize_t cbaf_wusb_host_band_groups_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - ssize_t result; - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - u16 band_groups = 0; - - result = sscanf(buf, "%04hx", &band_groups); - if (result != 1) - return -EINVAL; - - cbaf->host_band_groups = band_groups; - - return size; -} - -static DEVICE_ATTR(wusb_host_band_groups, 0600, - cbaf_wusb_host_band_groups_show, - cbaf_wusb_host_band_groups_store); - -static const struct wusb_cbaf_device_info cbaf_device_info_defaults = { - .Length_hdr = WUSB_AR_Length, - .CDID_hdr = WUSB_AR_CDID, - .BandGroups_hdr = WUSB_AR_BandGroups, - .LangID_hdr = WUSB_AR_LangID, - .DeviceFriendlyName_hdr = WUSB_AR_DeviceFriendlyName, -}; - -static ssize_t cbaf_wusb_cdid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - return sprintf(buf, "%16ph\n", cbaf->cdid.data); -} - -static ssize_t cbaf_wusb_cdid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - ssize_t result; - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - struct wusb_ckhdid cdid; - - result = sscanf(buf, - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx", - &cdid.data[0] , &cdid.data[1], - &cdid.data[2] , &cdid.data[3], - &cdid.data[4] , &cdid.data[5], - &cdid.data[6] , &cdid.data[7], - &cdid.data[8] , &cdid.data[9], - &cdid.data[10], &cdid.data[11], - &cdid.data[12], &cdid.data[13], - &cdid.data[14], &cdid.data[15]); - if (result != 16) - return -EINVAL; - - cbaf->cdid = cdid; - - return size; -} -static DEVICE_ATTR(wusb_cdid, 0600, cbaf_wusb_cdid_show, cbaf_wusb_cdid_store); - -static ssize_t cbaf_wusb_device_band_groups_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - return scnprintf(buf, PAGE_SIZE, "0x%04x\n", cbaf->device_band_groups); -} - -static DEVICE_ATTR(wusb_device_band_groups, 0600, - cbaf_wusb_device_band_groups_show, - NULL); - -static ssize_t cbaf_wusb_device_name_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - return scnprintf(buf, PAGE_SIZE, "%s\n", cbaf->device_name); -} -static DEVICE_ATTR(wusb_device_name, 0600, cbaf_wusb_device_name_show, NULL); - -static const struct wusb_cbaf_cc_data cbaf_cc_data_defaults = { - .AssociationTypeId_hdr = WUSB_AR_AssociationTypeId, - .AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB), - .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId, - .AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_ASSOCIATE), - .Length_hdr = WUSB_AR_Length, - .Length = cpu_to_le32(sizeof(struct wusb_cbaf_cc_data)), - .ConnectionContext_hdr = WUSB_AR_ConnectionContext, - .BandGroups_hdr = WUSB_AR_BandGroups, -}; - -static const struct wusb_cbaf_cc_data_fail cbaf_cc_data_fail_defaults = { - .AssociationTypeId_hdr = WUSB_AR_AssociationTypeId, - .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId, - .Length_hdr = WUSB_AR_Length, - .AssociationStatus_hdr = WUSB_AR_AssociationStatus, -}; - -/* - * Send a new CC to the device. - */ -static int cbaf_cc_upload(struct cbaf *cbaf) -{ - int result; - struct device *dev = &cbaf->usb_iface->dev; - struct wusb_cbaf_cc_data *ccd; - - ccd = cbaf->buffer; - *ccd = cbaf_cc_data_defaults; - ccd->CHID = cbaf->chid; - ccd->CDID = cbaf->cdid; - ccd->CK = cbaf->ck; - ccd->BandGroups = cpu_to_le16(cbaf->host_band_groups); - - dev_dbg(dev, "Trying to upload CC:\n"); - dev_dbg(dev, " CHID %16ph\n", ccd->CHID.data); - dev_dbg(dev, " CDID %16ph\n", ccd->CDID.data); - dev_dbg(dev, " Bandgroups 0x%04x\n", cbaf->host_band_groups); - - result = usb_control_msg( - cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0), - CBAF_REQ_SET_ASSOCIATION_RESPONSE, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0x0201, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, - ccd, sizeof(*ccd), USB_CTRL_SET_TIMEOUT); - - return result; -} - -static ssize_t cbaf_wusb_ck_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - ssize_t result; - struct usb_interface *iface = to_usb_interface(dev); - struct cbaf *cbaf = usb_get_intfdata(iface); - - result = sscanf(buf, - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx", - &cbaf->ck.data[0] , &cbaf->ck.data[1], - &cbaf->ck.data[2] , &cbaf->ck.data[3], - &cbaf->ck.data[4] , &cbaf->ck.data[5], - &cbaf->ck.data[6] , &cbaf->ck.data[7], - &cbaf->ck.data[8] , &cbaf->ck.data[9], - &cbaf->ck.data[10], &cbaf->ck.data[11], - &cbaf->ck.data[12], &cbaf->ck.data[13], - &cbaf->ck.data[14], &cbaf->ck.data[15]); - if (result != 16) - return -EINVAL; - - result = cbaf_cc_upload(cbaf); - if (result < 0) - return result; - - return size; -} -static DEVICE_ATTR(wusb_ck, 0600, NULL, cbaf_wusb_ck_store); - -static struct attribute *cbaf_dev_attrs[] = { - &dev_attr_wusb_host_name.attr, - &dev_attr_wusb_host_band_groups.attr, - &dev_attr_wusb_chid.attr, - &dev_attr_wusb_cdid.attr, - &dev_attr_wusb_device_name.attr, - &dev_attr_wusb_device_band_groups.attr, - &dev_attr_wusb_ck.attr, - NULL, -}; - -static const struct attribute_group cbaf_dev_attr_group = { - .name = NULL, /* we want them in the same directory */ - .attrs = cbaf_dev_attrs, -}; - -static int cbaf_probe(struct usb_interface *iface, - const struct usb_device_id *id) -{ - struct cbaf *cbaf; - struct device *dev = &iface->dev; - int result = -ENOMEM; - - cbaf = kzalloc(sizeof(*cbaf), GFP_KERNEL); - if (cbaf == NULL) - goto error_kzalloc; - cbaf->buffer = kmalloc(512, GFP_KERNEL); - if (cbaf->buffer == NULL) - goto error_kmalloc_buffer; - - cbaf->buffer_size = 512; - cbaf->usb_dev = usb_get_dev(interface_to_usbdev(iface)); - cbaf->usb_iface = usb_get_intf(iface); - result = cbaf_check(cbaf); - if (result < 0) { - dev_err(dev, "This device is not WUSB-CBAF compliant and is not supported yet.\n"); - goto error_check; - } - - result = sysfs_create_group(&dev->kobj, &cbaf_dev_attr_group); - if (result < 0) { - dev_err(dev, "Can't register sysfs attr group: %d\n", result); - goto error_create_group; - } - usb_set_intfdata(iface, cbaf); - return 0; - -error_create_group: -error_check: - usb_put_intf(iface); - usb_put_dev(cbaf->usb_dev); - kfree(cbaf->buffer); -error_kmalloc_buffer: - kfree(cbaf); -error_kzalloc: - return result; -} - -static void cbaf_disconnect(struct usb_interface *iface) -{ - struct cbaf *cbaf = usb_get_intfdata(iface); - struct device *dev = &iface->dev; - sysfs_remove_group(&dev->kobj, &cbaf_dev_attr_group); - usb_set_intfdata(iface, NULL); - usb_put_intf(iface); - usb_put_dev(cbaf->usb_dev); - kfree(cbaf->buffer); - /* paranoia: clean up crypto keys */ - kzfree(cbaf); -} - -static const struct usb_device_id cbaf_id_table[] = { - { USB_INTERFACE_INFO(0xef, 0x03, 0x01), }, - { }, -}; -MODULE_DEVICE_TABLE(usb, cbaf_id_table); - -static struct usb_driver cbaf_driver = { - .name = "wusb-cbaf", - .id_table = cbaf_id_table, - .probe = cbaf_probe, - .disconnect = cbaf_disconnect, -}; - -module_usb_driver(cbaf_driver); - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("Wireless USB Cable Based Association"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/wusbcore/crypto.c b/drivers/staging/wusbcore/crypto.c deleted file mode 100644 index d7d55ed19a98..000000000000 --- a/drivers/staging/wusbcore/crypto.c +++ /dev/null @@ -1,441 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Ultra Wide Band - * AES-128 CCM Encryption - * - * Copyright (C) 2007 Intel Corporation - * Inaky Perez-Gonzalez - * - * We don't do any encryption here; we use the Linux Kernel's AES-128 - * crypto modules to construct keys and payload blocks in a way - * defined by WUSB1.0[6]. Check the erratas, as typos are are patched - * there. - * - * Thanks a zillion to John Keys for his help and clarifications over - * the designed-by-a-committee text. - * - * So the idea is that there is this basic Pseudo-Random-Function - * defined in WUSB1.0[6.5] which is the core of everything. It works - * by tweaking some blocks, AES crypting them and then xoring - * something else with them (this seems to be called CBC(AES) -- can - * you tell I know jack about crypto?). So we just funnel it into the - * Linux Crypto API. - * - * We leave a crypto test module so we can verify that vectors match, - * every now and then. - * - * Block size: 16 bytes -- AES seems to do things in 'block sizes'. I - * am learning a lot... - * - * Conveniently, some data structures that need to be - * funneled through AES are...16 bytes in size! - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../uwb/uwb.h" -#include "include/wusb.h" - -static int debug_crypto_verify; - -module_param(debug_crypto_verify, int, 0); -MODULE_PARM_DESC(debug_crypto_verify, "verify the key generation algorithms"); - -static void wusb_key_dump(const void *buf, size_t len) -{ - print_hex_dump(KERN_ERR, " ", DUMP_PREFIX_OFFSET, 16, 1, - buf, len, 0); -} - -/* - * Block of data, as understood by AES-CCM - * - * The code assumes this structure is nothing but a 16 byte array - * (packed in a struct to avoid common mess ups that I usually do with - * arrays and enforcing type checking). - */ -struct aes_ccm_block { - u8 data[16]; -} __attribute__((packed)); - -/* - * Counter-mode Blocks (WUSB1.0[6.4]) - * - * According to CCM (or so it seems), for the purpose of calculating - * the MIC, the message is broken in N counter-mode blocks, B0, B1, - * ... BN. - * - * B0 contains flags, the CCM nonce and l(m). - * - * B1 contains l(a), the MAC header, the encryption offset and padding. - * - * If EO is nonzero, additional blocks are built from payload bytes - * until EO is exhausted (FIXME: padding to 16 bytes, I guess). The - * padding is not xmitted. - */ - -/* WUSB1.0[T6.4] */ -struct aes_ccm_b0 { - u8 flags; /* 0x59, per CCM spec */ - struct aes_ccm_nonce ccm_nonce; - __be16 lm; -} __attribute__((packed)); - -/* WUSB1.0[T6.5] */ -struct aes_ccm_b1 { - __be16 la; - u8 mac_header[10]; - __le16 eo; - u8 security_reserved; /* This is always zero */ - u8 padding; /* 0 */ -} __attribute__((packed)); - -/* - * Encryption Blocks (WUSB1.0[6.4.4]) - * - * CCM uses Ax blocks to generate a keystream with which the MIC and - * the message's payload are encoded. A0 always encrypts/decrypts the - * MIC. Ax (x>0) are used for the successive payload blocks. - * - * The x is the counter, and is increased for each block. - */ -struct aes_ccm_a { - u8 flags; /* 0x01, per CCM spec */ - struct aes_ccm_nonce ccm_nonce; - __be16 counter; /* Value of x */ -} __attribute__((packed)); - -/* Scratch space for MAC calculations. */ -struct wusb_mac_scratch { - struct aes_ccm_b0 b0; - struct aes_ccm_b1 b1; - struct aes_ccm_a ax; -}; - -/* - * CC-MAC function WUSB1.0[6.5] - * - * Take a data string and produce the encrypted CBC Counter-mode MIC - * - * Note the names for most function arguments are made to (more or - * less) match those used in the pseudo-function definition given in - * WUSB1.0[6.5]. - * - * @tfm_cbc: CBC(AES) blkcipher handle (initialized) - * - * @tfm_aes: AES cipher handle (initialized) - * - * @mic: buffer for placing the computed MIC (Message Integrity - * Code). This is exactly 8 bytes, and we expect the buffer to - * be at least eight bytes in length. - * - * @key: 128 bit symmetric key - * - * @n: CCM nonce - * - * @a: ASCII string, 14 bytes long (I guess zero padded if needed; - * we use exactly 14 bytes). - * - * @b: data stream to be processed - * - * @blen: size of b... - * - * Still not very clear how this is done, but looks like this: we - * create block B0 (as WUSB1.0[6.5] says), then we AES-crypt it with - * @key. We bytewise xor B0 with B1 (1) and AES-crypt that. Then we - * take the payload and divide it in blocks (16 bytes), xor them with - * the previous crypto result (16 bytes) and crypt it, repeat the next - * block with the output of the previous one, rinse wash. So we use - * the CBC-MAC(AES) shash, that does precisely that. The IV (Initial - * Vector) is 16 bytes and is set to zero, so - * - * (1) Created as 6.5 says, again, using as l(a) 'Blen + 14', and - * using the 14 bytes of @a to fill up - * b1.{mac_header,e0,security_reserved,padding}. - * - * NOTE: The definition of l(a) in WUSB1.0[6.5] vs the definition of - * l(m) is orthogonal, they bear no relationship, so it is not - * in conflict with the parameter's relation that - * WUSB1.0[6.4.2]) defines. - * - * NOTE: WUSB1.0[A.1]: Host Nonce is missing a nibble? (1e); fixed in - * first errata released on 2005/07. - * - * NOTE: we need to clean IV to zero at each invocation to make sure - * we start with a fresh empty Initial Vector, so that the CBC - * works ok. - * - * NOTE: blen is not aligned to a block size, we'll pad zeros, that's - * what sg[4] is for. Maybe there is a smarter way to do this. - */ -static int wusb_ccm_mac(struct crypto_shash *tfm_cbcmac, - struct wusb_mac_scratch *scratch, - void *mic, - const struct aes_ccm_nonce *n, - const struct aes_ccm_label *a, const void *b, - size_t blen) -{ - SHASH_DESC_ON_STACK(desc, tfm_cbcmac); - u8 iv[AES_BLOCK_SIZE]; - - /* - * These checks should be compile time optimized out - * ensure @a fills b1's mac_header and following fields - */ - BUILD_BUG_ON(sizeof(*a) != sizeof(scratch->b1) - sizeof(scratch->b1.la)); - BUILD_BUG_ON(sizeof(scratch->b0) != sizeof(struct aes_ccm_block)); - BUILD_BUG_ON(sizeof(scratch->b1) != sizeof(struct aes_ccm_block)); - BUILD_BUG_ON(sizeof(scratch->ax) != sizeof(struct aes_ccm_block)); - - /* Setup B0 */ - scratch->b0.flags = 0x59; /* Format B0 */ - scratch->b0.ccm_nonce = *n; - scratch->b0.lm = cpu_to_be16(0); /* WUSB1.0[6.5] sez l(m) is 0 */ - - /* Setup B1 - * - * The WUSB spec is anything but clear! WUSB1.0[6.5] - * says that to initialize B1 from A with 'l(a) = blen + - * 14'--after clarification, it means to use A's contents - * for MAC Header, EO, sec reserved and padding. - */ - scratch->b1.la = cpu_to_be16(blen + 14); - memcpy(&scratch->b1.mac_header, a, sizeof(*a)); - - desc->tfm = tfm_cbcmac; - crypto_shash_init(desc); - crypto_shash_update(desc, (u8 *)&scratch->b0, sizeof(scratch->b0) + - sizeof(scratch->b1)); - crypto_shash_finup(desc, b, blen, iv); - - /* Now we crypt the MIC Tag (*iv) with Ax -- values per WUSB1.0[6.5] - * The procedure is to AES crypt the A0 block and XOR the MIC - * Tag against it; we only do the first 8 bytes and place it - * directly in the destination buffer. - */ - scratch->ax.flags = 0x01; /* as per WUSB 1.0 spec */ - scratch->ax.ccm_nonce = *n; - scratch->ax.counter = 0; - - /* reuse the CBC-MAC transform to perform the single block encryption */ - crypto_shash_digest(desc, (u8 *)&scratch->ax, sizeof(scratch->ax), - (u8 *)&scratch->ax); - - crypto_xor_cpy(mic, (u8 *)&scratch->ax, iv, 8); - - return 8; -} - -/* - * WUSB Pseudo Random Function (WUSB1.0[6.5]) - * - * @b: buffer to the source data; cannot be a global or const local - * (will confuse the scatterlists) - */ -ssize_t wusb_prf(void *out, size_t out_size, - const u8 key[16], const struct aes_ccm_nonce *_n, - const struct aes_ccm_label *a, - const void *b, size_t blen, size_t len) -{ - ssize_t result, bytes = 0, bitr; - struct aes_ccm_nonce n = *_n; - struct crypto_shash *tfm_cbcmac; - struct wusb_mac_scratch scratch; - u64 sfn = 0; - __le64 sfn_le; - - tfm_cbcmac = crypto_alloc_shash("cbcmac(aes)", 0, 0); - if (IS_ERR(tfm_cbcmac)) { - result = PTR_ERR(tfm_cbcmac); - printk(KERN_ERR "E: can't load CBCMAC-AES: %d\n", (int)result); - goto error_alloc_cbcmac; - } - - result = crypto_shash_setkey(tfm_cbcmac, key, AES_BLOCK_SIZE); - if (result < 0) { - printk(KERN_ERR "E: can't set CBCMAC-AES key: %d\n", (int)result); - goto error_setkey_cbcmac; - } - - for (bitr = 0; bitr < (len + 63) / 64; bitr++) { - sfn_le = cpu_to_le64(sfn++); - memcpy(&n.sfn, &sfn_le, sizeof(n.sfn)); /* n.sfn++... */ - result = wusb_ccm_mac(tfm_cbcmac, &scratch, out + bytes, - &n, a, b, blen); - if (result < 0) - goto error_ccm_mac; - bytes += result; - } - result = bytes; - -error_ccm_mac: -error_setkey_cbcmac: - crypto_free_shash(tfm_cbcmac); -error_alloc_cbcmac: - return result; -} - -/* WUSB1.0[A.2] test vectors */ -static const u8 stv_hsmic_key[16] = { - 0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d, - 0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f -}; - -static const struct aes_ccm_nonce stv_hsmic_n = { - .sfn = { 0 }, - .tkid = { 0x76, 0x98, 0x01, }, - .dest_addr = { .data = { 0xbe, 0x00 } }, - .src_addr = { .data = { 0x76, 0x98 } }, -}; - -/* - * Out-of-band MIC Generation verification code - * - */ -static int wusb_oob_mic_verify(void) -{ - int result; - u8 mic[8]; - /* WUSB1.0[A.2] test vectors */ - static const struct usb_handshake stv_hsmic_hs = { - .bMessageNumber = 2, - .bStatus = 00, - .tTKID = { 0x76, 0x98, 0x01 }, - .bReserved = 00, - .CDID = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, - 0x3c, 0x3d, 0x3e, 0x3f }, - .nonce = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2e, 0x2f }, - .MIC = { 0x75, 0x6a, 0x97, 0x51, 0x0c, 0x8c, - 0x14, 0x7b }, - }; - size_t hs_size; - - result = wusb_oob_mic(mic, stv_hsmic_key, &stv_hsmic_n, &stv_hsmic_hs); - if (result < 0) - printk(KERN_ERR "E: WUSB OOB MIC test: failed: %d\n", result); - else if (memcmp(stv_hsmic_hs.MIC, mic, sizeof(mic))) { - printk(KERN_ERR "E: OOB MIC test: " - "mismatch between MIC result and WUSB1.0[A2]\n"); - hs_size = sizeof(stv_hsmic_hs) - sizeof(stv_hsmic_hs.MIC); - printk(KERN_ERR "E: Handshake2 in: (%zu bytes)\n", hs_size); - wusb_key_dump(&stv_hsmic_hs, hs_size); - printk(KERN_ERR "E: CCM Nonce in: (%zu bytes)\n", - sizeof(stv_hsmic_n)); - wusb_key_dump(&stv_hsmic_n, sizeof(stv_hsmic_n)); - printk(KERN_ERR "E: MIC out:\n"); - wusb_key_dump(mic, sizeof(mic)); - printk(KERN_ERR "E: MIC out (from WUSB1.0[A.2]):\n"); - wusb_key_dump(stv_hsmic_hs.MIC, sizeof(stv_hsmic_hs.MIC)); - result = -EINVAL; - } else - result = 0; - return result; -} - -/* - * Test vectors for Key derivation - * - * These come from WUSB1.0[6.5.1], the vectors in WUSB1.0[A.1] - * (errata corrected in 2005/07). - */ -static const u8 stv_key_a1[16] __attribute__ ((__aligned__(4))) = { - 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, - 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f -}; - -static const struct aes_ccm_nonce stv_keydvt_n_a1 = { - .sfn = { 0 }, - .tkid = { 0x76, 0x98, 0x01, }, - .dest_addr = { .data = { 0xbe, 0x00 } }, - .src_addr = { .data = { 0x76, 0x98 } }, -}; - -static const struct wusb_keydvt_out stv_keydvt_out_a1 = { - .kck = { - 0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d, - 0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f - }, - .ptk = { - 0xc8, 0x70, 0x62, 0x82, 0xb6, 0x7c, 0xe9, 0x06, - 0x7b, 0xc5, 0x25, 0x69, 0xf2, 0x36, 0x61, 0x2d - } -}; - -/* - * Performa a test to make sure we match the vectors defined in - * WUSB1.0[A.1](Errata2006/12) - */ -static int wusb_key_derive_verify(void) -{ - int result = 0; - struct wusb_keydvt_out keydvt_out; - /* These come from WUSB1.0[A.1] + 2006/12 errata */ - static const struct wusb_keydvt_in stv_keydvt_in_a1 = { - .hnonce = { - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f - }, - .dnonce = { - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f - } - }; - - result = wusb_key_derive(&keydvt_out, stv_key_a1, &stv_keydvt_n_a1, - &stv_keydvt_in_a1); - if (result < 0) - printk(KERN_ERR "E: WUSB key derivation test: " - "derivation failed: %d\n", result); - if (memcmp(&stv_keydvt_out_a1, &keydvt_out, sizeof(keydvt_out))) { - printk(KERN_ERR "E: WUSB key derivation test: " - "mismatch between key derivation result " - "and WUSB1.0[A1] Errata 2006/12\n"); - printk(KERN_ERR "E: keydvt in: key\n"); - wusb_key_dump(stv_key_a1, sizeof(stv_key_a1)); - printk(KERN_ERR "E: keydvt in: nonce\n"); - wusb_key_dump(&stv_keydvt_n_a1, sizeof(stv_keydvt_n_a1)); - printk(KERN_ERR "E: keydvt in: hnonce & dnonce\n"); - wusb_key_dump(&stv_keydvt_in_a1, sizeof(stv_keydvt_in_a1)); - printk(KERN_ERR "E: keydvt out: KCK\n"); - wusb_key_dump(&keydvt_out.kck, sizeof(keydvt_out.kck)); - printk(KERN_ERR "E: keydvt out: PTK\n"); - wusb_key_dump(&keydvt_out.ptk, sizeof(keydvt_out.ptk)); - result = -EINVAL; - } else - result = 0; - return result; -} - -/* - * Initialize crypto system - * - * FIXME: we do nothing now, other than verifying. Later on we'll - * cache the encryption stuff, so that's why we have a separate init. - */ -int wusb_crypto_init(void) -{ - int result; - - if (debug_crypto_verify) { - result = wusb_key_derive_verify(); - if (result < 0) - return result; - return wusb_oob_mic_verify(); - } - return 0; -} - -void wusb_crypto_exit(void) -{ - /* FIXME: free cached crypto transforms */ -} diff --git a/drivers/staging/wusbcore/dev-sysfs.c b/drivers/staging/wusbcore/dev-sysfs.c deleted file mode 100644 index 67b0a4c412b2..000000000000 --- a/drivers/staging/wusbcore/dev-sysfs.c +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * WUSB devices - * sysfs bindings - * - * Copyright (C) 2007 Intel Corporation - * Inaky Perez-Gonzalez - * - * Get them out of the way... - */ - -#include -#include -#include -#include "wusbhc.h" - -static ssize_t wusb_disconnect_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct usb_device *usb_dev; - struct wusbhc *wusbhc; - unsigned command; - u8 port_idx; - - if (sscanf(buf, "%u", &command) != 1) - return -EINVAL; - if (command == 0) - return size; - usb_dev = to_usb_device(dev); - wusbhc = wusbhc_get_by_usb_dev(usb_dev); - if (wusbhc == NULL) - return -ENODEV; - - mutex_lock(&wusbhc->mutex); - port_idx = wusb_port_no_to_idx(usb_dev->portnum); - __wusbhc_dev_disable(wusbhc, port_idx); - mutex_unlock(&wusbhc->mutex); - wusbhc_put(wusbhc); - return size; -} -static DEVICE_ATTR_WO(wusb_disconnect); - -static ssize_t wusb_cdid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - ssize_t result; - struct wusb_dev *wusb_dev; - - wusb_dev = wusb_dev_get_by_usb_dev(to_usb_device(dev)); - if (wusb_dev == NULL) - return -ENODEV; - result = sprintf(buf, "%16ph\n", wusb_dev->cdid.data); - wusb_dev_put(wusb_dev); - return result; -} -static DEVICE_ATTR_RO(wusb_cdid); - -static ssize_t wusb_ck_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int result; - struct usb_device *usb_dev; - struct wusbhc *wusbhc; - struct wusb_ckhdid ck; - - result = sscanf(buf, - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx\n", - &ck.data[0] , &ck.data[1], - &ck.data[2] , &ck.data[3], - &ck.data[4] , &ck.data[5], - &ck.data[6] , &ck.data[7], - &ck.data[8] , &ck.data[9], - &ck.data[10], &ck.data[11], - &ck.data[12], &ck.data[13], - &ck.data[14], &ck.data[15]); - if (result != 16) - return -EINVAL; - - usb_dev = to_usb_device(dev); - wusbhc = wusbhc_get_by_usb_dev(usb_dev); - if (wusbhc == NULL) - return -ENODEV; - result = wusb_dev_4way_handshake(wusbhc, usb_dev->wusb_dev, &ck); - memzero_explicit(&ck, sizeof(ck)); - wusbhc_put(wusbhc); - return result < 0 ? result : size; -} -static DEVICE_ATTR_WO(wusb_ck); - -static struct attribute *wusb_dev_attrs[] = { - &dev_attr_wusb_disconnect.attr, - &dev_attr_wusb_cdid.attr, - &dev_attr_wusb_ck.attr, - NULL, -}; - -static const struct attribute_group wusb_dev_attr_group = { - .name = NULL, /* we want them in the same directory */ - .attrs = wusb_dev_attrs, -}; - -int wusb_dev_sysfs_add(struct wusbhc *wusbhc, struct usb_device *usb_dev, - struct wusb_dev *wusb_dev) -{ - int result = sysfs_create_group(&usb_dev->dev.kobj, - &wusb_dev_attr_group); - struct device *dev = &usb_dev->dev; - if (result < 0) - dev_err(dev, "Cannot register WUSB-dev attributes: %d\n", - result); - return result; -} - -void wusb_dev_sysfs_rm(struct wusb_dev *wusb_dev) -{ - struct usb_device *usb_dev = wusb_dev->usb_dev; - if (usb_dev) - sysfs_remove_group(&usb_dev->dev.kobj, &wusb_dev_attr_group); -} diff --git a/drivers/staging/wusbcore/devconnect.c b/drivers/staging/wusbcore/devconnect.c deleted file mode 100644 index 1170f8baf608..000000000000 --- a/drivers/staging/wusbcore/devconnect.c +++ /dev/null @@ -1,1085 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) - * Device Connect handling - * - * Copyright (C) 2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - * FIXME: this file needs to be broken up, it's grown too big - * - * - * WUSB1.0[7.1, 7.5.1, ] - * - * WUSB device connection is kind of messy. Some background: - * - * When a device wants to connect it scans the UWB radio channels - * looking for a WUSB Channel; a WUSB channel is defined by MMCs - * (Micro Managed Commands or something like that) [see - * Design-overview for more on this] . - * - * So, device scans the radio, finds MMCs and thus a host and checks - * when the next DNTS is. It sends a Device Notification Connect - * (DN_Connect); the host picks it up (through nep.c and notif.c, ends - * up in wusb_devconnect_ack(), which creates a wusb_dev structure in - * wusbhc->port[port_number].wusb_dev), assigns an unauth address - * to the device (this means from 0x80 to 0xfe) and sends, in the MMC - * a Connect Ack Information Element (ConnAck IE). - * - * So now the device now has a WUSB address. From now on, we use - * that to talk to it in the RPipes. - * - * ASSUMPTIONS: - * - * - We use the the as device address the port number where it is - * connected (port 0 doesn't exist). For unauth, it is 128 + that. - * - * ROADMAP: - * - * This file contains the logic for doing that--entry points: - * - * wusb_devconnect_ack() Ack a device until _acked() called. - * Called by notif.c:wusb_handle_dn_connect() - * when a DN_Connect is received. - * - * wusb_devconnect_acked() Ack done, release resources. - * - * wusb_handle_dn_alive() Called by notif.c:wusb_handle_dn() - * for processing a DN_Alive pong from a device. - * - * wusb_handle_dn_disconnect()Called by notif.c:wusb_handle_dn() to - * process a disconnect request from a - * device. - * - * __wusb_dev_disable() Called by rh.c:wusbhc_rh_clear_port_feat() when - * disabling a port. - * - * wusb_devconnect_create() Called when creating the host by - * lc.c:wusbhc_create(). - * - * wusb_devconnect_destroy() Cleanup called removing the host. Called - * by lc.c:wusbhc_destroy(). - * - * Each Wireless USB host maintains a list of DN_Connect requests - * (actually we maintain a list of pending Connect Acks, the - * wusbhc->ca_list). - * - * LIFE CYCLE OF port->wusb_dev - * - * Before the @wusbhc structure put()s the reference it owns for - * port->wusb_dev [and clean the wusb_dev pointer], it needs to - * lock @wusbhc->mutex. - */ - -#include -#include -#include -#include -#include -#include "wusbhc.h" - -static void wusbhc_devconnect_acked_work(struct work_struct *work); - -static void wusb_dev_free(struct wusb_dev *wusb_dev) -{ - kfree(wusb_dev); -} - -static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc) -{ - struct wusb_dev *wusb_dev; - - wusb_dev = kzalloc(sizeof(*wusb_dev), GFP_KERNEL); - if (wusb_dev == NULL) - goto err; - - wusb_dev->wusbhc = wusbhc; - - INIT_WORK(&wusb_dev->devconnect_acked_work, wusbhc_devconnect_acked_work); - - return wusb_dev; -err: - wusb_dev_free(wusb_dev); - return NULL; -} - - -/* - * Using the Connect-Ack list, fill out the @wusbhc Connect-Ack WUSB IE - * properly so that it can be added to the MMC. - * - * We just get the @wusbhc->ca_list and fill out the first four ones or - * less (per-spec WUSB1.0[7.5, before T7-38). If the ConnectAck WUSB - * IE is not allocated, we alloc it. - * - * @wusbhc->mutex must be taken - */ -static void wusbhc_fill_cack_ie(struct wusbhc *wusbhc) -{ - unsigned cnt; - struct wusb_dev *dev_itr; - struct wuie_connect_ack *cack_ie; - - cack_ie = &wusbhc->cack_ie; - cnt = 0; - list_for_each_entry(dev_itr, &wusbhc->cack_list, cack_node) { - cack_ie->blk[cnt].CDID = dev_itr->cdid; - cack_ie->blk[cnt].bDeviceAddress = dev_itr->addr; - if (++cnt >= WUIE_ELT_MAX) - break; - } - cack_ie->hdr.bLength = sizeof(cack_ie->hdr) - + cnt * sizeof(cack_ie->blk[0]); -} - -/* - * Register a new device that wants to connect - * - * A new device wants to connect, so we add it to the Connect-Ack - * list. We give it an address in the unauthorized range (bit 8 set); - * user space will have to drive authorization further on. - * - * @dev_addr: address to use for the device (which is also the port - * number). - * - * @wusbhc->mutex must be taken - */ -static struct wusb_dev *wusbhc_cack_add(struct wusbhc *wusbhc, - struct wusb_dn_connect *dnc, - const char *pr_cdid, u8 port_idx) -{ - struct device *dev = wusbhc->dev; - struct wusb_dev *wusb_dev; - int new_connection = wusb_dn_connect_new_connection(dnc); - u8 dev_addr; - int result; - - /* Is it registered already? */ - list_for_each_entry(wusb_dev, &wusbhc->cack_list, cack_node) - if (!memcmp(&wusb_dev->cdid, &dnc->CDID, - sizeof(wusb_dev->cdid))) - return wusb_dev; - /* We don't have it, create an entry, register it */ - wusb_dev = wusb_dev_alloc(wusbhc); - if (wusb_dev == NULL) - return NULL; - wusb_dev_init(wusb_dev); - wusb_dev->cdid = dnc->CDID; - wusb_dev->port_idx = port_idx; - - /* - * Devices are always available within the cluster reservation - * and since the hardware will take the intersection of the - * per-device availability and the cluster reservation, the - * per-device availability can simply be set to always - * available. - */ - bitmap_fill(wusb_dev->availability.bm, UWB_NUM_MAS); - - /* FIXME: handle reconnects instead of assuming connects are - always new. */ - if (1 && new_connection == 0) - new_connection = 1; - if (new_connection) { - dev_addr = (port_idx + 2) | WUSB_DEV_ADDR_UNAUTH; - - dev_info(dev, "Connecting new WUSB device to address %u, " - "port %u\n", dev_addr, port_idx); - - result = wusb_set_dev_addr(wusbhc, wusb_dev, dev_addr); - if (result < 0) - return NULL; - } - wusb_dev->entry_ts = jiffies; - list_add_tail(&wusb_dev->cack_node, &wusbhc->cack_list); - wusbhc->cack_count++; - wusbhc_fill_cack_ie(wusbhc); - - return wusb_dev; -} - -/* - * Remove a Connect-Ack context entry from the HCs view - * - * @wusbhc->mutex must be taken - */ -static void wusbhc_cack_rm(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) -{ - list_del_init(&wusb_dev->cack_node); - wusbhc->cack_count--; - wusbhc_fill_cack_ie(wusbhc); -} - -/* - * @wusbhc->mutex must be taken */ -static -void wusbhc_devconnect_acked(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) -{ - wusbhc_cack_rm(wusbhc, wusb_dev); - if (wusbhc->cack_count) - wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->cack_ie.hdr); - else - wusbhc_mmcie_rm(wusbhc, &wusbhc->cack_ie.hdr); -} - -static void wusbhc_devconnect_acked_work(struct work_struct *work) -{ - struct wusb_dev *wusb_dev = container_of(work, struct wusb_dev, - devconnect_acked_work); - struct wusbhc *wusbhc = wusb_dev->wusbhc; - - mutex_lock(&wusbhc->mutex); - wusbhc_devconnect_acked(wusbhc, wusb_dev); - mutex_unlock(&wusbhc->mutex); - - wusb_dev_put(wusb_dev); -} - -/* - * Ack a device for connection - * - * FIXME: docs - * - * @pr_cdid: Printable CDID...hex Use @dnc->cdid for the real deal. - * - * So we get the connect ack IE (may have been allocated already), - * find an empty connect block, an empty virtual port, create an - * address with it (see below), make it an unauth addr [bit 7 set] and - * set the MMC. - * - * Addresses: because WUSB hosts have no downstream hubs, we can do a - * 1:1 mapping between 'port number' and device - * address. This simplifies many things, as during this - * initial connect phase the USB stack has no knowledge of - * the device and hasn't assigned an address yet--we know - * USB's choose_address() will use the same heuristics we - * use here, so we can assume which address will be assigned. - * - * USB stack always assigns address 1 to the root hub, so - * to the port number we add 2 (thus virtual port #0 is - * addr #2). - * - * @wusbhc shall be referenced - */ -static -void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc, - const char *pr_cdid) -{ - int result; - struct device *dev = wusbhc->dev; - struct wusb_dev *wusb_dev; - struct wusb_port *port; - unsigned idx; - - mutex_lock(&wusbhc->mutex); - - /* Check we are not handling it already */ - for (idx = 0; idx < wusbhc->ports_max; idx++) { - port = wusb_port_by_idx(wusbhc, idx); - if (port->wusb_dev - && memcmp(&dnc->CDID, &port->wusb_dev->cdid, sizeof(dnc->CDID)) == 0) - goto error_unlock; - } - /* Look up those fake ports we have for a free one */ - for (idx = 0; idx < wusbhc->ports_max; idx++) { - port = wusb_port_by_idx(wusbhc, idx); - if ((port->status & USB_PORT_STAT_POWER) - && !(port->status & USB_PORT_STAT_CONNECTION)) - break; - } - if (idx >= wusbhc->ports_max) { - dev_err(dev, "Host controller can't connect more devices " - "(%u already connected); device %s rejected\n", - wusbhc->ports_max, pr_cdid); - /* NOTE: we could send a WUIE_Disconnect here, but we haven't - * event acked, so the device will eventually timeout the - * connection, right? */ - goto error_unlock; - } - - /* Make sure we are using no crypto on that "virtual port" */ - wusbhc->set_ptk(wusbhc, idx, 0, NULL, 0); - - /* Grab a filled in Connect-Ack context, fill out the - * Connect-Ack Wireless USB IE, set the MMC */ - wusb_dev = wusbhc_cack_add(wusbhc, dnc, pr_cdid, idx); - if (wusb_dev == NULL) - goto error_unlock; - result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->cack_ie.hdr); - if (result < 0) - goto error_unlock; - /* Give the device at least 2ms (WUSB1.0[7.5.1p3]), let's do - * three for a good measure */ - msleep(3); - port->wusb_dev = wusb_dev; - port->status |= USB_PORT_STAT_CONNECTION; - port->change |= USB_PORT_STAT_C_CONNECTION; - /* Now the port status changed to connected; hub_wq will - * pick the change up and try to reset the port to bring it to - * the enabled state--so this process returns up to the stack - * and it calls back into wusbhc_rh_port_reset(). - */ -error_unlock: - mutex_unlock(&wusbhc->mutex); - return; - -} - -/* - * Disconnect a Wireless USB device from its fake port - * - * Marks the port as disconnected so that hub_wq can pick up the change - * and drops our knowledge about the device. - * - * Assumes there is a device connected - * - * @port_index: zero based port number - * - * NOTE: @wusbhc->mutex is locked - * - * WARNING: From here it is not very safe to access anything hanging off - * wusb_dev - */ -static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc, - struct wusb_port *port) -{ - struct wusb_dev *wusb_dev = port->wusb_dev; - - port->status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE - | USB_PORT_STAT_SUSPEND | USB_PORT_STAT_RESET - | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); - port->change |= USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE; - if (wusb_dev) { - dev_dbg(wusbhc->dev, "disconnecting device from port %d\n", wusb_dev->port_idx); - if (!list_empty(&wusb_dev->cack_node)) - list_del_init(&wusb_dev->cack_node); - /* For the one in cack_add() */ - wusb_dev_put(wusb_dev); - } - port->wusb_dev = NULL; - - /* After a device disconnects, change the GTK (see [WUSB] - * section 6.2.11.2). */ - if (wusbhc->active) - wusbhc_gtk_rekey(wusbhc); - - /* The Wireless USB part has forgotten about the device already; now - * hub_wq's timer will pick up the disconnection and remove the USB - * device from the system - */ -} - -/* - * Refresh the list of keep alives to emit in the MMC - * - * We only publish the first four devices that have a coming timeout - * condition. Then when we are done processing those, we go for the - * next ones. We ignore the ones that have timed out already (they'll - * be purged). - * - * This might cause the first devices to timeout the last devices in - * the port array...FIXME: come up with a better algorithm? - * - * Note we can't do much about MMC's ops errors; we hope next refresh - * will kind of handle it. - * - * NOTE: @wusbhc->mutex is locked - */ -static void __wusbhc_keep_alive(struct wusbhc *wusbhc) -{ - struct device *dev = wusbhc->dev; - unsigned cnt; - struct wusb_dev *wusb_dev; - struct wusb_port *wusb_port; - struct wuie_keep_alive *ie = &wusbhc->keep_alive_ie; - unsigned keep_alives, old_keep_alives; - - old_keep_alives = ie->hdr.bLength - sizeof(ie->hdr); - keep_alives = 0; - for (cnt = 0; - keep_alives < WUIE_ELT_MAX && cnt < wusbhc->ports_max; - cnt++) { - unsigned tt = msecs_to_jiffies(wusbhc->trust_timeout); - - wusb_port = wusb_port_by_idx(wusbhc, cnt); - wusb_dev = wusb_port->wusb_dev; - - if (wusb_dev == NULL) - continue; - if (wusb_dev->usb_dev == NULL) - continue; - - if (time_after(jiffies, wusb_dev->entry_ts + tt)) { - dev_err(dev, "KEEPALIVE: device %u timed out\n", - wusb_dev->addr); - __wusbhc_dev_disconnect(wusbhc, wusb_port); - } else if (time_after(jiffies, wusb_dev->entry_ts + tt/3)) { - /* Approaching timeout cut off, need to refresh */ - ie->bDeviceAddress[keep_alives++] = wusb_dev->addr; - } - } - if (keep_alives & 0x1) /* pad to even number ([WUSB] section 7.5.9) */ - ie->bDeviceAddress[keep_alives++] = 0x7f; - ie->hdr.bLength = sizeof(ie->hdr) + - keep_alives*sizeof(ie->bDeviceAddress[0]); - if (keep_alives > 0) - wusbhc_mmcie_set(wusbhc, 10, 5, &ie->hdr); - else if (old_keep_alives != 0) - wusbhc_mmcie_rm(wusbhc, &ie->hdr); -} - -/* - * Do a run through all devices checking for timeouts - */ -static void wusbhc_keep_alive_run(struct work_struct *ws) -{ - struct delayed_work *dw = to_delayed_work(ws); - struct wusbhc *wusbhc = container_of(dw, struct wusbhc, keep_alive_timer); - - mutex_lock(&wusbhc->mutex); - __wusbhc_keep_alive(wusbhc); - mutex_unlock(&wusbhc->mutex); - - queue_delayed_work(wusbd, &wusbhc->keep_alive_timer, - msecs_to_jiffies(wusbhc->trust_timeout / 2)); -} - -/* - * Find the wusb_dev from its device address. - * - * The device can be found directly from the address (see - * wusb_cack_add() for where the device address is set to port_idx - * +2), except when the address is zero. - */ -static struct wusb_dev *wusbhc_find_dev_by_addr(struct wusbhc *wusbhc, u8 addr) -{ - int p; - - if (addr == 0xff) /* unconnected */ - return NULL; - - if (addr > 0) { - int port = (addr & ~0x80) - 2; - if (port < 0 || port >= wusbhc->ports_max) - return NULL; - return wusb_port_by_idx(wusbhc, port)->wusb_dev; - } - - /* Look for the device with address 0. */ - for (p = 0; p < wusbhc->ports_max; p++) { - struct wusb_dev *wusb_dev = wusb_port_by_idx(wusbhc, p)->wusb_dev; - if (wusb_dev && wusb_dev->addr == addr) - return wusb_dev; - } - return NULL; -} - -/* - * Handle a DN_Alive notification (WUSB1.0[7.6.1]) - * - * This just updates the device activity timestamp and then refreshes - * the keep alive IE. - * - * @wusbhc shall be referenced and unlocked - */ -static void wusbhc_handle_dn_alive(struct wusbhc *wusbhc, u8 srcaddr) -{ - struct wusb_dev *wusb_dev; - - mutex_lock(&wusbhc->mutex); - wusb_dev = wusbhc_find_dev_by_addr(wusbhc, srcaddr); - if (wusb_dev == NULL) { - dev_dbg(wusbhc->dev, "ignoring DN_Alive from unconnected device %02x\n", - srcaddr); - } else { - wusb_dev->entry_ts = jiffies; - __wusbhc_keep_alive(wusbhc); - } - mutex_unlock(&wusbhc->mutex); -} - -/* - * Handle a DN_Connect notification (WUSB1.0[7.6.1]) - * - * @wusbhc - * @pkt_hdr - * @size: Size of the buffer where the notification resides; if the - * notification data suggests there should be more data than - * available, an error will be signaled and the whole buffer - * consumed. - * - * @wusbhc->mutex shall be held - */ -static void wusbhc_handle_dn_connect(struct wusbhc *wusbhc, - struct wusb_dn_hdr *dn_hdr, - size_t size) -{ - struct device *dev = wusbhc->dev; - struct wusb_dn_connect *dnc; - char pr_cdid[WUSB_CKHDID_STRSIZE]; - static const char *beacon_behaviour[] = { - "reserved", - "self-beacon", - "directed-beacon", - "no-beacon" - }; - - if (size < sizeof(*dnc)) { - dev_err(dev, "DN CONNECT: short notification (%zu < %zu)\n", - size, sizeof(*dnc)); - return; - } - - dnc = container_of(dn_hdr, struct wusb_dn_connect, hdr); - sprintf(pr_cdid, "%16ph", dnc->CDID.data); - dev_info(dev, "DN CONNECT: device %s @ %x (%s) wants to %s\n", - pr_cdid, - wusb_dn_connect_prev_dev_addr(dnc), - beacon_behaviour[wusb_dn_connect_beacon_behavior(dnc)], - wusb_dn_connect_new_connection(dnc) ? "connect" : "reconnect"); - /* ACK the connect */ - wusbhc_devconnect_ack(wusbhc, dnc, pr_cdid); -} - -/* - * Handle a DN_Disconnect notification (WUSB1.0[7.6.1]) - * - * Device is going down -- do the disconnect. - * - * @wusbhc shall be referenced and unlocked - */ -static void wusbhc_handle_dn_disconnect(struct wusbhc *wusbhc, u8 srcaddr) -{ - struct device *dev = wusbhc->dev; - struct wusb_dev *wusb_dev; - - mutex_lock(&wusbhc->mutex); - wusb_dev = wusbhc_find_dev_by_addr(wusbhc, srcaddr); - if (wusb_dev == NULL) { - dev_dbg(dev, "ignoring DN DISCONNECT from unconnected device %02x\n", - srcaddr); - } else { - dev_info(dev, "DN DISCONNECT: device 0x%02x going down\n", - wusb_dev->addr); - __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, - wusb_dev->port_idx)); - } - mutex_unlock(&wusbhc->mutex); -} - -/* - * Handle a Device Notification coming a host - * - * The Device Notification comes from a host (HWA, DWA or WHCI) - * wrapped in a set of headers. Somebody else has peeled off those - * headers for us and we just get one Device Notifications. - * - * Invalid DNs (e.g., too short) are discarded. - * - * @wusbhc shall be referenced - * - * FIXMES: - * - implement priorities as in WUSB1.0[Table 7-55]? - */ -void wusbhc_handle_dn(struct wusbhc *wusbhc, u8 srcaddr, - struct wusb_dn_hdr *dn_hdr, size_t size) -{ - struct device *dev = wusbhc->dev; - - if (size < sizeof(struct wusb_dn_hdr)) { - dev_err(dev, "DN data shorter than DN header (%d < %d)\n", - (int)size, (int)sizeof(struct wusb_dn_hdr)); - return; - } - switch (dn_hdr->bType) { - case WUSB_DN_CONNECT: - wusbhc_handle_dn_connect(wusbhc, dn_hdr, size); - break; - case WUSB_DN_ALIVE: - wusbhc_handle_dn_alive(wusbhc, srcaddr); - break; - case WUSB_DN_DISCONNECT: - wusbhc_handle_dn_disconnect(wusbhc, srcaddr); - break; - case WUSB_DN_MASAVAILCHANGED: - case WUSB_DN_RWAKE: - case WUSB_DN_SLEEP: - /* FIXME: handle these DNs. */ - break; - case WUSB_DN_EPRDY: - /* The hardware handles these. */ - break; - default: - dev_warn(dev, "unknown DN %u (%d octets) from %u\n", - dn_hdr->bType, (int)size, srcaddr); - } -} -EXPORT_SYMBOL_GPL(wusbhc_handle_dn); - -/* - * Disconnect a WUSB device from a the cluster - * - * @wusbhc - * @port Fake port where the device is (wusbhc index, not USB port number). - * - * In Wireless USB, a disconnect is basically telling the device he is - * being disconnected and forgetting about him. - * - * We send the device a Device Disconnect IE (WUSB1.0[7.5.11]) for 100 - * ms and then keep going. - * - * We don't do much in case of error; we always pretend we disabled - * the port and disconnected the device. If physically the request - * didn't get there (many things can fail in the way there), the stack - * will reject the device's communication attempts. - * - * @wusbhc should be refcounted and locked - */ -void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port_idx) -{ - int result; - struct device *dev = wusbhc->dev; - struct wusb_dev *wusb_dev; - struct wuie_disconnect *ie; - - wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev; - if (wusb_dev == NULL) { - /* reset no device? ignore */ - dev_dbg(dev, "DISCONNECT: no device at port %u, ignoring\n", - port_idx); - return; - } - __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx)); - - ie = kzalloc(sizeof(*ie), GFP_KERNEL); - if (ie == NULL) - return; - ie->hdr.bLength = sizeof(*ie); - ie->hdr.bIEIdentifier = WUIE_ID_DEVICE_DISCONNECT; - ie->bDeviceAddress = wusb_dev->addr; - result = wusbhc_mmcie_set(wusbhc, 0, 0, &ie->hdr); - if (result < 0) - dev_err(dev, "DISCONNECT: can't set MMC: %d\n", result); - else { - /* At least 6 MMCs, assuming at least 1 MMC per zone. */ - msleep(7*4); - wusbhc_mmcie_rm(wusbhc, &ie->hdr); - } - kfree(ie); -} - -/* - * Walk over the BOS descriptor, verify and grok it - * - * @usb_dev: referenced - * @wusb_dev: referenced and unlocked - * - * The BOS descriptor is defined at WUSB1.0[7.4.1], and it defines a - * "flexible" way to wrap all kinds of descriptors inside an standard - * descriptor (wonder why they didn't use normal descriptors, - * btw). Not like they lack code. - * - * At the end we go to look for the WUSB Device Capabilities - * (WUSB1.0[7.4.1.1]) that is wrapped in a device capability descriptor - * that is part of the BOS descriptor set. That tells us what does the - * device support (dual role, beacon type, UWB PHY rates). - */ -static int wusb_dev_bos_grok(struct usb_device *usb_dev, - struct wusb_dev *wusb_dev, - struct usb_bos_descriptor *bos, size_t desc_size) -{ - ssize_t result; - struct device *dev = &usb_dev->dev; - void *itr, *top; - - /* Walk over BOS capabilities, verify them */ - itr = (void *)bos + sizeof(*bos); - top = itr + desc_size - sizeof(*bos); - while (itr < top) { - struct usb_dev_cap_header *cap_hdr = itr; - size_t cap_size; - u8 cap_type; - if (top - itr < sizeof(*cap_hdr)) { - dev_err(dev, "Device BUG? premature end of BOS header " - "data [offset 0x%02x]: only %zu bytes left\n", - (int)(itr - (void *)bos), top - itr); - result = -ENOSPC; - goto error_bad_cap; - } - cap_size = cap_hdr->bLength; - cap_type = cap_hdr->bDevCapabilityType; - if (cap_size == 0) - break; - if (cap_size > top - itr) { - dev_err(dev, "Device BUG? premature end of BOS data " - "[offset 0x%02x cap %02x %zu bytes]: " - "only %zu bytes left\n", - (int)(itr - (void *)bos), - cap_type, cap_size, top - itr); - result = -EBADF; - goto error_bad_cap; - } - switch (cap_type) { - case USB_CAP_TYPE_WIRELESS_USB: - if (cap_size != sizeof(*wusb_dev->wusb_cap_descr)) - dev_err(dev, "Device BUG? WUSB Capability " - "descriptor is %zu bytes vs %zu " - "needed\n", cap_size, - sizeof(*wusb_dev->wusb_cap_descr)); - else - wusb_dev->wusb_cap_descr = itr; - break; - default: - dev_err(dev, "BUG? Unknown BOS capability 0x%02x " - "(%zu bytes) at offset 0x%02x\n", cap_type, - cap_size, (int)(itr - (void *)bos)); - } - itr += cap_size; - } - result = 0; -error_bad_cap: - return result; -} - -/* - * Add information from the BOS descriptors to the device - * - * @usb_dev: referenced - * @wusb_dev: referenced and unlocked - * - * So what we do is we alloc a space for the BOS descriptor of 64 - * bytes; read the first four bytes which include the wTotalLength - * field (WUSB1.0[T7-26]) and if it fits in those 64 bytes, read the - * whole thing. If not we realloc to that size. - * - * Then we call the groking function, that will fill up - * wusb_dev->wusb_cap_descr, which is what we'll need later on. - */ -static int wusb_dev_bos_add(struct usb_device *usb_dev, - struct wusb_dev *wusb_dev) -{ - ssize_t result; - struct device *dev = &usb_dev->dev; - struct usb_bos_descriptor *bos; - size_t alloc_size = 32, desc_size = 4; - - bos = kmalloc(alloc_size, GFP_KERNEL); - if (bos == NULL) - return -ENOMEM; - result = usb_get_descriptor(usb_dev, USB_DT_BOS, 0, bos, desc_size); - if (result < 4) { - dev_err(dev, "Can't get BOS descriptor or too short: %zd\n", - result); - goto error_get_descriptor; - } - desc_size = le16_to_cpu(bos->wTotalLength); - if (desc_size >= alloc_size) { - kfree(bos); - alloc_size = desc_size; - bos = kmalloc(alloc_size, GFP_KERNEL); - if (bos == NULL) - return -ENOMEM; - } - result = usb_get_descriptor(usb_dev, USB_DT_BOS, 0, bos, desc_size); - if (result < 0 || result != desc_size) { - dev_err(dev, "Can't get BOS descriptor or too short (need " - "%zu bytes): %zd\n", desc_size, result); - goto error_get_descriptor; - } - if (result < sizeof(*bos) - || le16_to_cpu(bos->wTotalLength) != desc_size) { - dev_err(dev, "Can't get BOS descriptor or too short (need " - "%zu bytes): %zd\n", desc_size, result); - goto error_get_descriptor; - } - - result = wusb_dev_bos_grok(usb_dev, wusb_dev, bos, result); - if (result < 0) - goto error_bad_bos; - wusb_dev->bos = bos; - return 0; - -error_bad_bos: -error_get_descriptor: - kfree(bos); - wusb_dev->wusb_cap_descr = NULL; - return result; -} - -static void wusb_dev_bos_rm(struct wusb_dev *wusb_dev) -{ - kfree(wusb_dev->bos); - wusb_dev->wusb_cap_descr = NULL; -}; - -/* - * USB stack's device addition Notifier Callback - * - * Called from drivers/usb/core/hub.c when a new device is added; we - * use this hook to perform certain WUSB specific setup work on the - * new device. As well, it is the first time we can connect the - * wusb_dev and the usb_dev. So we note it down in wusb_dev and take a - * reference that we'll drop. - * - * First we need to determine if the device is a WUSB device (else we - * ignore it). For that we use the speed setting (USB_SPEED_WIRELESS) - * [FIXME: maybe we'd need something more definitive]. If so, we track - * it's usb_busd and from there, the WUSB HC. - * - * Because all WUSB HCs are contained in a 'struct wusbhc', voila, we - * get the wusbhc for the device. - * - * We have a reference on @usb_dev (as we are called at the end of its - * enumeration). - * - * NOTE: @usb_dev locked - */ -static void wusb_dev_add_ncb(struct usb_device *usb_dev) -{ - int result = 0; - struct wusb_dev *wusb_dev; - struct wusbhc *wusbhc; - struct device *dev = &usb_dev->dev; - u8 port_idx; - - if (usb_dev->wusb == 0 || usb_dev->devnum == 1) - return; /* skip non wusb and wusb RHs */ - - usb_set_device_state(usb_dev, USB_STATE_UNAUTHENTICATED); - - wusbhc = wusbhc_get_by_usb_dev(usb_dev); - if (wusbhc == NULL) - goto error_nodev; - mutex_lock(&wusbhc->mutex); - wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev); - port_idx = wusb_port_no_to_idx(usb_dev->portnum); - mutex_unlock(&wusbhc->mutex); - if (wusb_dev == NULL) - goto error_nodev; - wusb_dev->usb_dev = usb_get_dev(usb_dev); - usb_dev->wusb_dev = wusb_dev_get(wusb_dev); - result = wusb_dev_sec_add(wusbhc, usb_dev, wusb_dev); - if (result < 0) { - dev_err(dev, "Cannot enable security: %d\n", result); - goto error_sec_add; - } - /* Now query the device for it's BOS and attach it to wusb_dev */ - result = wusb_dev_bos_add(usb_dev, wusb_dev); - if (result < 0) { - dev_err(dev, "Cannot get BOS descriptors: %d\n", result); - goto error_bos_add; - } - result = wusb_dev_sysfs_add(wusbhc, usb_dev, wusb_dev); - if (result < 0) - goto error_add_sysfs; -out: - wusb_dev_put(wusb_dev); - wusbhc_put(wusbhc); -error_nodev: - return; - -error_add_sysfs: - wusb_dev_bos_rm(wusb_dev); -error_bos_add: - wusb_dev_sec_rm(wusb_dev); -error_sec_add: - mutex_lock(&wusbhc->mutex); - __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx)); - mutex_unlock(&wusbhc->mutex); - goto out; -} - -/* - * Undo all the steps done at connection by the notifier callback - * - * NOTE: @usb_dev locked - */ -static void wusb_dev_rm_ncb(struct usb_device *usb_dev) -{ - struct wusb_dev *wusb_dev = usb_dev->wusb_dev; - - if (usb_dev->wusb == 0 || usb_dev->devnum == 1) - return; /* skip non wusb and wusb RHs */ - - wusb_dev_sysfs_rm(wusb_dev); - wusb_dev_bos_rm(wusb_dev); - wusb_dev_sec_rm(wusb_dev); - wusb_dev->usb_dev = NULL; - usb_dev->wusb_dev = NULL; - wusb_dev_put(wusb_dev); - usb_put_dev(usb_dev); -} - -/* - * Handle notifications from the USB stack (notifier call back) - * - * This is called when the USB stack does a - * usb_{bus,device}_{add,remove}() so we can do WUSB specific - * handling. It is called with [for the case of - * USB_DEVICE_{ADD,REMOVE} with the usb_dev locked. - */ -int wusb_usb_ncb(struct notifier_block *nb, unsigned long val, - void *priv) -{ - int result = NOTIFY_OK; - - switch (val) { - case USB_DEVICE_ADD: - wusb_dev_add_ncb(priv); - break; - case USB_DEVICE_REMOVE: - wusb_dev_rm_ncb(priv); - break; - case USB_BUS_ADD: - /* ignore (for now) */ - case USB_BUS_REMOVE: - break; - default: - WARN_ON(1); - result = NOTIFY_BAD; - } - return result; -} - -/* - * Return a referenced wusb_dev given a @wusbhc and @usb_dev - */ -struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *wusbhc, - struct usb_device *usb_dev) -{ - struct wusb_dev *wusb_dev; - u8 port_idx; - - port_idx = wusb_port_no_to_idx(usb_dev->portnum); - BUG_ON(port_idx > wusbhc->ports_max); - wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev; - if (wusb_dev != NULL) /* ops, device is gone */ - wusb_dev_get(wusb_dev); - return wusb_dev; -} -EXPORT_SYMBOL_GPL(__wusb_dev_get_by_usb_dev); - -void wusb_dev_destroy(struct kref *_wusb_dev) -{ - struct wusb_dev *wusb_dev = container_of(_wusb_dev, struct wusb_dev, refcnt); - - list_del_init(&wusb_dev->cack_node); - wusb_dev_free(wusb_dev); -} -EXPORT_SYMBOL_GPL(wusb_dev_destroy); - -/* - * Create all the device connect handling infrastructure - * - * This is basically the device info array, Connect Acknowledgement - * (cack) lists, keep-alive timers (and delayed work thread). - */ -int wusbhc_devconnect_create(struct wusbhc *wusbhc) -{ - wusbhc->keep_alive_ie.hdr.bIEIdentifier = WUIE_ID_KEEP_ALIVE; - wusbhc->keep_alive_ie.hdr.bLength = sizeof(wusbhc->keep_alive_ie.hdr); - INIT_DELAYED_WORK(&wusbhc->keep_alive_timer, wusbhc_keep_alive_run); - - wusbhc->cack_ie.hdr.bIEIdentifier = WUIE_ID_CONNECTACK; - wusbhc->cack_ie.hdr.bLength = sizeof(wusbhc->cack_ie.hdr); - INIT_LIST_HEAD(&wusbhc->cack_list); - - return 0; -} - -/* - * Release all resources taken by the devconnect stuff - */ -void wusbhc_devconnect_destroy(struct wusbhc *wusbhc) -{ - /* no op */ -} - -/* - * wusbhc_devconnect_start - start accepting device connections - * @wusbhc: the WUSB HC - * - * Sets the Host Info IE to accept all new connections. - * - * FIXME: This also enables the keep alives but this is not necessary - * until there are connected and authenticated devices. - */ -int wusbhc_devconnect_start(struct wusbhc *wusbhc) -{ - struct device *dev = wusbhc->dev; - struct wuie_host_info *hi; - int result; - - hi = kzalloc(sizeof(*hi), GFP_KERNEL); - if (hi == NULL) - return -ENOMEM; - - hi->hdr.bLength = sizeof(*hi); - hi->hdr.bIEIdentifier = WUIE_ID_HOST_INFO; - hi->attributes = cpu_to_le16((wusbhc->rsv->stream << 3) | WUIE_HI_CAP_ALL); - hi->CHID = wusbhc->chid; - result = wusbhc_mmcie_set(wusbhc, 0, 0, &hi->hdr); - if (result < 0) { - dev_err(dev, "Cannot add Host Info MMCIE: %d\n", result); - goto error_mmcie_set; - } - wusbhc->wuie_host_info = hi; - - queue_delayed_work(wusbd, &wusbhc->keep_alive_timer, - msecs_to_jiffies(wusbhc->trust_timeout / 2)); - - return 0; - -error_mmcie_set: - kfree(hi); - return result; -} - -/* - * wusbhc_devconnect_stop - stop managing connected devices - * @wusbhc: the WUSB HC - * - * Disconnects any devices still connected, stops the keep alives and - * removes the Host Info IE. - */ -void wusbhc_devconnect_stop(struct wusbhc *wusbhc) -{ - int i; - - mutex_lock(&wusbhc->mutex); - for (i = 0; i < wusbhc->ports_max; i++) { - if (wusbhc->port[i].wusb_dev) - __wusbhc_dev_disconnect(wusbhc, &wusbhc->port[i]); - } - mutex_unlock(&wusbhc->mutex); - - cancel_delayed_work_sync(&wusbhc->keep_alive_timer); - wusbhc_mmcie_rm(wusbhc, &wusbhc->wuie_host_info->hdr); - kfree(wusbhc->wuie_host_info); - wusbhc->wuie_host_info = NULL; -} - -/* - * wusb_set_dev_addr - set the WUSB device address used by the host - * @wusbhc: the WUSB HC the device is connect to - * @wusb_dev: the WUSB device - * @addr: new device address - */ -int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, u8 addr) -{ - int result; - - wusb_dev->addr = addr; - result = wusbhc->dev_info_set(wusbhc, wusb_dev); - if (result < 0) - dev_err(wusbhc->dev, "device %d: failed to set device " - "address\n", wusb_dev->port_idx); - else - dev_info(wusbhc->dev, "device %d: %s addr %u\n", - wusb_dev->port_idx, - (addr & WUSB_DEV_ADDR_UNAUTH) ? "unauth" : "auth", - wusb_dev->addr); - - return result; -} diff --git a/drivers/staging/wusbcore/host/Kconfig b/drivers/staging/wusbcore/host/Kconfig deleted file mode 100644 index 9a73f9360a08..000000000000 --- a/drivers/staging/wusbcore/host/Kconfig +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -config USB_WHCI_HCD - tristate "Wireless USB Host Controller Interface (WHCI) driver" - depends on USB_PCI && USB && UWB - select USB_WUSB - select UWB_WHCI - help - A driver for PCI-based Wireless USB Host Controllers that are - compliant with the WHCI specification. - - To compile this driver a module, choose M here: the module - will be called "whci-hcd". - -config USB_HWA_HCD - tristate "Host Wire Adapter (HWA) driver" - depends on USB && UWB - select USB_WUSB - select UWB_HWA - help - This driver enables you to connect Wireless USB devices to - your system using a Host Wire Adaptor USB dongle. This is an - UWB Radio Controller and WUSB Host Controller connected to - your machine via USB (specified in WUSB1.0). - - To compile this driver a module, choose M here: the module - will be called "hwa-hc". - diff --git a/drivers/staging/wusbcore/host/Makefile b/drivers/staging/wusbcore/host/Makefile deleted file mode 100644 index d65ee8a73e21..000000000000 --- a/drivers/staging/wusbcore/host/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_USB_WHCI_HCD) += whci/ -obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o diff --git a/drivers/staging/wusbcore/host/hwa-hc.c b/drivers/staging/wusbcore/host/hwa-hc.c deleted file mode 100644 index 8d959e91fe27..000000000000 --- a/drivers/staging/wusbcore/host/hwa-hc.c +++ /dev/null @@ -1,875 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Host Wire Adapter: - * Driver glue, HWA-specific functions, bridges to WAHC and WUSBHC - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * The HWA driver is a simple layer that forwards requests to the WAHC - * (Wire Adater Host Controller) or WUSBHC (Wireless USB Host - * Controller) layers. - * - * Host Wire Adapter is the 'WUSB 1.0 standard' name for Wireless-USB - * Host Controller that is connected to your system via USB (a USB - * dongle that implements a USB host...). There is also a Device Wired - * Adaptor, DWA (Wireless USB hub) that uses the same mechanism for - * transferring data (it is after all a USB host connected via - * Wireless USB), we have a common layer called Wire Adapter Host - * Controller that does all the hard work. The WUSBHC (Wireless USB - * Host Controller) is the part common to WUSB Host Controllers, the - * HWA and the PCI-based one, that is implemented following the WHCI - * spec. All these layers are implemented in ../wusbcore. - * - * The main functions are hwahc_op_urb_{en,de}queue(), that pass the - * job of converting a URB to a Wire Adapter - * - * Entry points: - * - * hwahc_driver_*() Driver initialization, registration and - * teardown. - * - * hwahc_probe() New device came up, create an instance for - * it [from device enumeration]. - * - * hwahc_disconnect() Remove device instance [from device - * enumeration]. - * - * [__]hwahc_op_*() Host-Wire-Adaptor specific functions for - * starting/stopping/etc (some might be made also - * DWA). - */ -#include -#include -#include -#include -#include -#include -#include "../wa-hc.h" -#include "../wusbhc.h" - -struct hwahc { - struct wusbhc wusbhc; /* has to be 1st */ - struct wahc wa; -}; - -/* - * FIXME should be wusbhc - * - * NOTE: we need to cache the Cluster ID because later...there is no - * way to get it :) - */ -static int __hwahc_set_cluster_id(struct hwahc *hwahc, u8 cluster_id) -{ - int result; - struct wusbhc *wusbhc = &hwahc->wusbhc; - struct wahc *wa = &hwahc->wa; - struct device *dev = &wa->usb_iface->dev; - - result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - WUSB_REQ_SET_CLUSTER_ID, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - cluster_id, - wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, USB_CTRL_SET_TIMEOUT); - if (result < 0) - dev_err(dev, "Cannot set WUSB Cluster ID to 0x%02x: %d\n", - cluster_id, result); - else - wusbhc->cluster_id = cluster_id; - dev_info(dev, "Wireless USB Cluster ID set to 0x%02x\n", cluster_id); - return result; -} - -static int __hwahc_op_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots) -{ - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - - return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - WUSB_REQ_SET_NUM_DNTS, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - interval << 8 | slots, - wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, USB_CTRL_SET_TIMEOUT); -} - -/* - * Reset a WUSB host controller and wait for it to complete doing it. - * - * @usb_hcd: Pointer to WUSB Host Controller instance. - * - */ -static int hwahc_op_reset(struct usb_hcd *usb_hcd) -{ - int result; - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct device *dev = &hwahc->wa.usb_iface->dev; - - mutex_lock(&wusbhc->mutex); - wa_nep_disarm(&hwahc->wa); - result = __wa_set_feature(&hwahc->wa, WA_RESET); - if (result < 0) { - dev_err(dev, "error commanding HC to reset: %d\n", result); - goto error_unlock; - } - result = __wa_wait_status(&hwahc->wa, WA_STATUS_RESETTING, 0); - if (result < 0) { - dev_err(dev, "error waiting for HC to reset: %d\n", result); - goto error_unlock; - } -error_unlock: - mutex_unlock(&wusbhc->mutex); - return result; -} - -/* - * FIXME: break this function up - */ -static int hwahc_op_start(struct usb_hcd *usb_hcd) -{ - u8 addr; - int result; - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - - result = -ENOSPC; - mutex_lock(&wusbhc->mutex); - addr = wusb_cluster_id_get(); - if (addr == 0) - goto error_cluster_id_get; - result = __hwahc_set_cluster_id(hwahc, addr); - if (result < 0) - goto error_set_cluster_id; - - usb_hcd->uses_new_polling = 1; - set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); - usb_hcd->state = HC_STATE_RUNNING; - - /* - * prevent USB core from suspending the root hub since - * bus_suspend and bus_resume are not yet supported. - */ - pm_runtime_get_noresume(&usb_hcd->self.root_hub->dev); - - result = 0; -out: - mutex_unlock(&wusbhc->mutex); - return result; - -error_set_cluster_id: - wusb_cluster_id_put(addr); -error_cluster_id_get: - goto out; - -} - -/* - * No need to abort pipes, as when this is called, all the children - * has been disconnected and that has done it [through - * usb_disable_interface() -> usb_disable_endpoint() -> - * hwahc_op_ep_disable() - >rpipe_ep_disable()]. - */ -static void hwahc_op_stop(struct usb_hcd *usb_hcd) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - - mutex_lock(&wusbhc->mutex); - wusb_cluster_id_put(wusbhc->cluster_id); - mutex_unlock(&wusbhc->mutex); -} - -static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - - /* - * We cannot query the HWA for the WUSB time since that requires sending - * a synchronous URB and this function can be called in_interrupt. - * Instead, query the USB frame number for our parent and use that. - */ - return usb_get_current_frame_number(wa->usb_dev); -} - -static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, - gfp_t gfp) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - - return wa_urb_enqueue(&hwahc->wa, urb->ep, urb, gfp); -} - -static int hwahc_op_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, - int status) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - - return wa_urb_dequeue(&hwahc->wa, urb, status); -} - -/* - * Release resources allocated for an endpoint - * - * If there is an associated rpipe to this endpoint, go ahead and put it. - */ -static void hwahc_op_endpoint_disable(struct usb_hcd *usb_hcd, - struct usb_host_endpoint *ep) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - - rpipe_ep_disable(&hwahc->wa, ep); -} - -static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc) -{ - int result; - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct device *dev = &hwahc->wa.usb_iface->dev; - - result = __wa_set_feature(&hwahc->wa, WA_ENABLE); - if (result < 0) { - dev_err(dev, "error commanding HC to start: %d\n", result); - goto error_stop; - } - result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE); - if (result < 0) { - dev_err(dev, "error waiting for HC to start: %d\n", result); - goto error_stop; - } - result = wa_nep_arm(&hwahc->wa, GFP_KERNEL); - if (result < 0) { - dev_err(dev, "cannot listen to notifications: %d\n", result); - goto error_stop; - } - /* - * If WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS is set, - * disable transfer notifications. - */ - if (hwahc->wa.quirks & - WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS) { - struct usb_host_interface *cur_altsetting = - hwahc->wa.usb_iface->cur_altsetting; - - result = usb_control_msg(hwahc->wa.usb_dev, - usb_sndctrlpipe(hwahc->wa.usb_dev, 0), - WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE, - WA_REQ_ALEREON_FEATURE_SET, - cur_altsetting->desc.bInterfaceNumber, - NULL, 0, - USB_CTRL_SET_TIMEOUT); - /* - * If we successfully sent the control message, start DTI here - * because no transfer notifications will be received which is - * where DTI is normally started. - */ - if (result == 0) - result = wa_dti_start(&hwahc->wa); - else - result = 0; /* OK. Continue normally. */ - - if (result < 0) { - dev_err(dev, "cannot start DTI: %d\n", result); - goto error_dti_start; - } - } - - return result; - -error_dti_start: - wa_nep_disarm(&hwahc->wa); -error_stop: - __wa_clear_feature(&hwahc->wa, WA_ENABLE); - return result; -} - -static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc, int delay) -{ - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; - int ret; - - ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - WUSB_REQ_CHAN_STOP, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - delay * 1000, - iface_no, - NULL, 0, USB_CTRL_SET_TIMEOUT); - if (ret == 0) - msleep(delay); - - wa_nep_disarm(&hwahc->wa); - __wa_stop(&hwahc->wa); -} - -/* - * Set the UWB MAS allocation for the WUSB cluster - * - * @stream_index: stream to use (-1 for cancelling the allocation) - * @mas: mas bitmap to use - */ -static int __hwahc_op_bwa_set(struct wusbhc *wusbhc, s8 stream_index, - const struct uwb_mas_bm *mas) -{ - int result; - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - struct device *dev = &wa->usb_iface->dev; - u8 mas_le[UWB_NUM_MAS/8]; - - /* Set the stream index */ - result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - WUSB_REQ_SET_STREAM_IDX, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - stream_index, - wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, USB_CTRL_SET_TIMEOUT); - if (result < 0) { - dev_err(dev, "Cannot set WUSB stream index: %d\n", result); - goto out; - } - uwb_mas_bm_copy_le(mas_le, mas); - /* Set the MAS allocation */ - result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - WUSB_REQ_SET_WUSB_MAS, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - mas_le, 32, USB_CTRL_SET_TIMEOUT); - if (result < 0) - dev_err(dev, "Cannot set WUSB MAS allocation: %d\n", result); -out: - return result; -} - -/* - * Add an IE to the host's MMC - * - * @interval: See WUSB1.0[8.5.3.1] - * @repeat_cnt: See WUSB1.0[8.5.3.1] - * @handle: See WUSB1.0[8.5.3.1] - * @wuie: Pointer to the header of the WUSB IE data to add. - * MUST BE allocated in a kmalloc buffer (no stack or - * vmalloc). - * - * NOTE: the format of the WUSB IEs for MMCs are different to the - * normal MBOA MAC IEs (IE Id + Length in MBOA MAC vs. Length + - * Id in WUSB IEs). Standards...you gotta love'em. - */ -static int __hwahc_op_mmcie_add(struct wusbhc *wusbhc, u8 interval, - u8 repeat_cnt, u8 handle, - struct wuie_hdr *wuie) -{ - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; - - return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - WUSB_REQ_ADD_MMC_IE, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - interval << 8 | repeat_cnt, - handle << 8 | iface_no, - wuie, wuie->bLength, USB_CTRL_SET_TIMEOUT); -} - -/* - * Remove an IE to the host's MMC - * - * @handle: See WUSB1.0[8.5.3.1] - */ -static int __hwahc_op_mmcie_rm(struct wusbhc *wusbhc, u8 handle) -{ - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; - return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - WUSB_REQ_REMOVE_MMC_IE, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, handle << 8 | iface_no, - NULL, 0, USB_CTRL_SET_TIMEOUT); -} - -/* - * Update device information for a given fake port - * - * @port_idx: Fake port to which device is connected (wusbhc index, not - * USB port number). - */ -static int __hwahc_op_dev_info_set(struct wusbhc *wusbhc, - struct wusb_dev *wusb_dev) -{ - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; - struct hwa_dev_info *dev_info; - int ret; - - /* fill out the Device Info buffer and send it */ - dev_info = kzalloc(sizeof(struct hwa_dev_info), GFP_KERNEL); - if (!dev_info) - return -ENOMEM; - uwb_mas_bm_copy_le(dev_info->bmDeviceAvailability, - &wusb_dev->availability); - dev_info->bDeviceAddress = wusb_dev->addr; - - /* - * If the descriptors haven't been read yet, use a default PHY - * rate of 53.3 Mbit/s only. The correct value will be used - * when this will be called again as part of the - * authentication process (which occurs after the descriptors - * have been read). - */ - if (wusb_dev->wusb_cap_descr) - dev_info->wPHYRates = wusb_dev->wusb_cap_descr->wPHYRates; - else - dev_info->wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53); - - ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - WUSB_REQ_SET_DEV_INFO, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, wusb_dev->port_idx << 8 | iface_no, - dev_info, sizeof(struct hwa_dev_info), - USB_CTRL_SET_TIMEOUT); - kfree(dev_info); - return ret; -} - -/* - * Set host's idea of which encryption (and key) method to use when - * talking to ad evice on a given port. - * - * If key is NULL, it means disable encryption for that "virtual port" - * (used when we disconnect). - */ -static int __hwahc_dev_set_key(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, - const void *key, size_t key_size, - u8 key_idx) -{ - int result = -ENOMEM; - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; - struct usb_key_descriptor *keyd; - size_t keyd_len; - - keyd_len = sizeof(*keyd) + key_size; - keyd = kzalloc(keyd_len, GFP_KERNEL); - if (keyd == NULL) - return -ENOMEM; - - keyd->bLength = keyd_len; - keyd->bDescriptorType = USB_DT_KEY; - keyd->tTKID[0] = (tkid >> 0) & 0xff; - keyd->tTKID[1] = (tkid >> 8) & 0xff; - keyd->tTKID[2] = (tkid >> 16) & 0xff; - memcpy(keyd->bKeyData, key, key_size); - - result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - USB_REQ_SET_DESCRIPTOR, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - USB_DT_KEY << 8 | key_idx, - port_idx << 8 | iface_no, - keyd, keyd_len, USB_CTRL_SET_TIMEOUT); - - kzfree(keyd); /* clear keys etc. */ - return result; -} - -/* - * Set host's idea of which encryption (and key) method to use when - * talking to ad evice on a given port. - * - * If key is NULL, it means disable encryption for that "virtual port" - * (used when we disconnect). - */ -static int __hwahc_op_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, - const void *key, size_t key_size) -{ - int result = -ENOMEM; - struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); - struct wahc *wa = &hwahc->wa; - u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; - u8 encryption_value; - - /* Tell the host which key to use to talk to the device */ - if (key) { - u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_PTK, - WUSB_KEY_INDEX_ORIGINATOR_HOST); - - result = __hwahc_dev_set_key(wusbhc, port_idx, tkid, - key, key_size, key_idx); - if (result < 0) - goto error_set_key; - encryption_value = wusbhc->ccm1_etd->bEncryptionValue; - } else { - /* FIXME: this should come from wusbhc->etd[UNSECURE].value */ - encryption_value = 0; - } - - /* Set the encryption type for communicating with the device */ - result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - USB_REQ_SET_ENCRYPTION, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - encryption_value, port_idx << 8 | iface_no, - NULL, 0, USB_CTRL_SET_TIMEOUT); - if (result < 0) - dev_err(wusbhc->dev, "Can't set host's WUSB encryption for " - "port index %u to %s (value %d): %d\n", port_idx, - wusb_et_name(wusbhc->ccm1_etd->bEncryptionType), - wusbhc->ccm1_etd->bEncryptionValue, result); -error_set_key: - return result; -} - -/* - * Set host's GTK key - */ -static int __hwahc_op_set_gtk(struct wusbhc *wusbhc, u32 tkid, - const void *key, size_t key_size) -{ - u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK, - WUSB_KEY_INDEX_ORIGINATOR_HOST); - - return __hwahc_dev_set_key(wusbhc, 0, tkid, key, key_size, key_idx); -} - -/* - * Get the Wire Adapter class-specific descriptor - * - * NOTE: this descriptor comes with the big bundled configuration - * descriptor that includes the interfaces' and endpoints', so - * we just look for it in the cached copy kept by the USB stack. - * - * NOTE2: We convert LE fields to CPU order. - */ -static int wa_fill_descr(struct wahc *wa) -{ - int result; - struct device *dev = &wa->usb_iface->dev; - char *itr; - struct usb_device *usb_dev = wa->usb_dev; - struct usb_descriptor_header *hdr; - struct usb_wa_descriptor *wa_descr; - size_t itr_size, actconfig_idx; - - actconfig_idx = (usb_dev->actconfig - usb_dev->config) / - sizeof(usb_dev->config[0]); - itr = usb_dev->rawdescriptors[actconfig_idx]; - itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength); - while (itr_size >= sizeof(*hdr)) { - hdr = (struct usb_descriptor_header *) itr; - dev_dbg(dev, "Extra device descriptor: " - "type %02x/%u bytes @ %zu (%zu left)\n", - hdr->bDescriptorType, hdr->bLength, - (itr - usb_dev->rawdescriptors[actconfig_idx]), - itr_size); - if (hdr->bDescriptorType == USB_DT_WIRE_ADAPTER) - goto found; - itr += hdr->bLength; - itr_size -= hdr->bLength; - } - dev_err(dev, "cannot find Wire Adapter Class descriptor\n"); - return -ENODEV; - -found: - result = -EINVAL; - if (hdr->bLength > itr_size) { /* is it available? */ - dev_err(dev, "incomplete Wire Adapter Class descriptor " - "(%zu bytes left, %u needed)\n", - itr_size, hdr->bLength); - goto error; - } - if (hdr->bLength < sizeof(*wa->wa_descr)) { - dev_err(dev, "short Wire Adapter Class descriptor\n"); - goto error; - } - wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr; - if (le16_to_cpu(wa_descr->bcdWAVersion) > 0x0100) - dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n", - (le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00) >> 8, - le16_to_cpu(wa_descr->bcdWAVersion) & 0x00ff); - result = 0; -error: - return result; -} - -static const struct hc_driver hwahc_hc_driver = { - .description = "hwa-hcd", - .product_desc = "Wireless USB HWA host controller", - .hcd_priv_size = sizeof(struct hwahc) - sizeof(struct usb_hcd), - .irq = NULL, /* FIXME */ - .flags = HCD_USB25, - .reset = hwahc_op_reset, - .start = hwahc_op_start, - .stop = hwahc_op_stop, - .get_frame_number = hwahc_op_get_frame_number, - .urb_enqueue = hwahc_op_urb_enqueue, - .urb_dequeue = hwahc_op_urb_dequeue, - .endpoint_disable = hwahc_op_endpoint_disable, - - .hub_status_data = wusbhc_rh_status_data, - .hub_control = wusbhc_rh_control, - .start_port_reset = wusbhc_rh_start_port_reset, -}; - -static int hwahc_security_create(struct hwahc *hwahc) -{ - int result; - struct wusbhc *wusbhc = &hwahc->wusbhc; - struct usb_device *usb_dev = hwahc->wa.usb_dev; - struct device *dev = &usb_dev->dev; - struct usb_security_descriptor *secd; - struct usb_encryption_descriptor *etd; - void *itr, *top; - size_t itr_size, needed, bytes; - u8 index; - char buf[64]; - - /* Find the host's security descriptors in the config descr bundle */ - index = (usb_dev->actconfig - usb_dev->config) / - sizeof(usb_dev->config[0]); - itr = usb_dev->rawdescriptors[index]; - itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength); - top = itr + itr_size; - result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index], - le16_to_cpu(usb_dev->actconfig->desc.wTotalLength), - USB_DT_SECURITY, (void **) &secd, sizeof(*secd)); - if (result == -1) { - dev_warn(dev, "BUG? WUSB host has no security descriptors\n"); - return 0; - } - needed = sizeof(*secd); - if (top - (void *)secd < needed) { - dev_err(dev, "BUG? Not enough data to process security " - "descriptor header (%zu bytes left vs %zu needed)\n", - top - (void *) secd, needed); - return 0; - } - needed = le16_to_cpu(secd->wTotalLength); - if (top - (void *)secd < needed) { - dev_err(dev, "BUG? Not enough data to process security " - "descriptors (%zu bytes left vs %zu needed)\n", - top - (void *) secd, needed); - return 0; - } - /* Walk over the sec descriptors and store CCM1's on wusbhc */ - itr = (void *) secd + sizeof(*secd); - top = (void *) secd + le16_to_cpu(secd->wTotalLength); - index = 0; - bytes = 0; - while (itr < top) { - etd = itr; - if (top - itr < sizeof(*etd)) { - dev_err(dev, "BUG: bad host security descriptor; " - "not enough data (%zu vs %zu left)\n", - top - itr, sizeof(*etd)); - break; - } - if (etd->bLength < sizeof(*etd)) { - dev_err(dev, "BUG: bad host encryption descriptor; " - "descriptor is too short " - "(%zu vs %zu needed)\n", - (size_t)etd->bLength, sizeof(*etd)); - break; - } - itr += etd->bLength; - bytes += snprintf(buf + bytes, sizeof(buf) - bytes, - "%s (0x%02x) ", - wusb_et_name(etd->bEncryptionType), - etd->bEncryptionValue); - wusbhc->ccm1_etd = etd; - } - dev_info(dev, "supported encryption types: %s\n", buf); - if (wusbhc->ccm1_etd == NULL) { - dev_err(dev, "E: host doesn't support CCM-1 crypto\n"); - return 0; - } - /* Pretty print what we support */ - return 0; -} - -static void hwahc_security_release(struct hwahc *hwahc) -{ - /* nothing to do here so far... */ -} - -static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface, - kernel_ulong_t quirks) -{ - int result; - struct device *dev = &iface->dev; - struct wusbhc *wusbhc = &hwahc->wusbhc; - struct wahc *wa = &hwahc->wa; - struct usb_device *usb_dev = interface_to_usbdev(iface); - - wa->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */ - wa->usb_iface = usb_get_intf(iface); - wusbhc->dev = dev; - /* defer getting the uwb_rc handle until it is needed since it - * may not have been registered by the hwa_rc driver yet. */ - wusbhc->uwb_rc = NULL; - result = wa_fill_descr(wa); /* Get the device descriptor */ - if (result < 0) - goto error_fill_descriptor; - if (wa->wa_descr->bNumPorts > USB_MAXCHILDREN) { - dev_err(dev, "FIXME: USB_MAXCHILDREN too low for WUSB " - "adapter (%u ports)\n", wa->wa_descr->bNumPorts); - wusbhc->ports_max = USB_MAXCHILDREN; - } else { - wusbhc->ports_max = wa->wa_descr->bNumPorts; - } - wusbhc->mmcies_max = wa->wa_descr->bNumMMCIEs; - wusbhc->start = __hwahc_op_wusbhc_start; - wusbhc->stop = __hwahc_op_wusbhc_stop; - wusbhc->mmcie_add = __hwahc_op_mmcie_add; - wusbhc->mmcie_rm = __hwahc_op_mmcie_rm; - wusbhc->dev_info_set = __hwahc_op_dev_info_set; - wusbhc->bwa_set = __hwahc_op_bwa_set; - wusbhc->set_num_dnts = __hwahc_op_set_num_dnts; - wusbhc->set_ptk = __hwahc_op_set_ptk; - wusbhc->set_gtk = __hwahc_op_set_gtk; - result = hwahc_security_create(hwahc); - if (result < 0) { - dev_err(dev, "Can't initialize security: %d\n", result); - goto error_security_create; - } - wa->wusb = wusbhc; /* FIXME: ugly, need to fix */ - result = wusbhc_create(&hwahc->wusbhc); - if (result < 0) { - dev_err(dev, "Can't create WUSB HC structures: %d\n", result); - goto error_wusbhc_create; - } - result = wa_create(&hwahc->wa, iface, quirks); - if (result < 0) - goto error_wa_create; - return 0; - -error_wa_create: - wusbhc_destroy(&hwahc->wusbhc); -error_wusbhc_create: - /* WA Descr fill allocs no resources */ -error_security_create: -error_fill_descriptor: - usb_put_intf(iface); - usb_put_dev(usb_dev); - return result; -} - -static void hwahc_destroy(struct hwahc *hwahc) -{ - struct wusbhc *wusbhc = &hwahc->wusbhc; - - mutex_lock(&wusbhc->mutex); - __wa_destroy(&hwahc->wa); - wusbhc_destroy(&hwahc->wusbhc); - hwahc_security_release(hwahc); - hwahc->wusbhc.dev = NULL; - uwb_rc_put(wusbhc->uwb_rc); - usb_put_intf(hwahc->wa.usb_iface); - usb_put_dev(hwahc->wa.usb_dev); - mutex_unlock(&wusbhc->mutex); -} - -static void hwahc_init(struct hwahc *hwahc) -{ - wa_init(&hwahc->wa); -} - -static int hwahc_probe(struct usb_interface *usb_iface, - const struct usb_device_id *id) -{ - int result; - struct usb_hcd *usb_hcd; - struct wusbhc *wusbhc; - struct hwahc *hwahc; - struct device *dev = &usb_iface->dev; - - result = -ENOMEM; - usb_hcd = usb_create_hcd(&hwahc_hc_driver, &usb_iface->dev, "wusb-hwa"); - if (usb_hcd == NULL) { - dev_err(dev, "unable to allocate instance\n"); - goto error_alloc; - } - usb_hcd->wireless = 1; - usb_hcd->self.sg_tablesize = ~0; - wusbhc = usb_hcd_to_wusbhc(usb_hcd); - hwahc = container_of(wusbhc, struct hwahc, wusbhc); - hwahc_init(hwahc); - result = hwahc_create(hwahc, usb_iface, id->driver_info); - if (result < 0) { - dev_err(dev, "Cannot initialize internals: %d\n", result); - goto error_hwahc_create; - } - result = usb_add_hcd(usb_hcd, 0, 0); - if (result < 0) { - dev_err(dev, "Cannot add HCD: %d\n", result); - goto error_add_hcd; - } - device_wakeup_enable(usb_hcd->self.controller); - result = wusbhc_b_create(&hwahc->wusbhc); - if (result < 0) { - dev_err(dev, "Cannot setup phase B of WUSBHC: %d\n", result); - goto error_wusbhc_b_create; - } - return 0; - -error_wusbhc_b_create: - usb_remove_hcd(usb_hcd); -error_add_hcd: - hwahc_destroy(hwahc); -error_hwahc_create: - usb_put_hcd(usb_hcd); -error_alloc: - return result; -} - -static void hwahc_disconnect(struct usb_interface *usb_iface) -{ - struct usb_hcd *usb_hcd; - struct wusbhc *wusbhc; - struct hwahc *hwahc; - - usb_hcd = usb_get_intfdata(usb_iface); - wusbhc = usb_hcd_to_wusbhc(usb_hcd); - hwahc = container_of(wusbhc, struct hwahc, wusbhc); - - wusbhc_b_destroy(&hwahc->wusbhc); - usb_remove_hcd(usb_hcd); - hwahc_destroy(hwahc); - usb_put_hcd(usb_hcd); -} - -static const struct usb_device_id hwahc_id_table[] = { - /* Alereon 5310 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01), - .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC | - WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS }, - /* Alereon 5611 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01), - .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC | - WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS }, - /* FIXME: use class labels for this */ - { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), }, - {}, -}; -MODULE_DEVICE_TABLE(usb, hwahc_id_table); - -static struct usb_driver hwahc_driver = { - .name = "hwa-hc", - .probe = hwahc_probe, - .disconnect = hwahc_disconnect, - .id_table = hwahc_id_table, -}; - -module_usb_driver(hwahc_driver); - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("Host Wired Adapter USB Host Control Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/wusbcore/host/whci/Makefile b/drivers/staging/wusbcore/host/whci/Makefile deleted file mode 100644 index 859d20079df6..000000000000 --- a/drivers/staging/wusbcore/host/whci/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_USB_WHCI_HCD) += whci-hcd.o - -whci-hcd-y := \ - asl.o \ - debug.o \ - hcd.o \ - hw.o \ - init.o \ - int.o \ - pzl.o \ - qset.o \ - wusb.o diff --git a/drivers/staging/wusbcore/host/whci/asl.c b/drivers/staging/wusbcore/host/whci/asl.c deleted file mode 100644 index a2b9a50cfb80..000000000000 --- a/drivers/staging/wusbcore/host/whci/asl.c +++ /dev/null @@ -1,376 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) asynchronous schedule management. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include - -#include "../../../uwb/include/umc.h" -#include "../../wusbhc.h" - -#include "whcd.h" - -static void qset_get_next_prev(struct whc *whc, struct whc_qset *qset, - struct whc_qset **next, struct whc_qset **prev) -{ - struct list_head *n, *p; - - BUG_ON(list_empty(&whc->async_list)); - - n = qset->list_node.next; - if (n == &whc->async_list) - n = n->next; - p = qset->list_node.prev; - if (p == &whc->async_list) - p = p->prev; - - *next = container_of(n, struct whc_qset, list_node); - *prev = container_of(p, struct whc_qset, list_node); - -} - -static void asl_qset_insert_begin(struct whc *whc, struct whc_qset *qset) -{ - list_move(&qset->list_node, &whc->async_list); - qset->in_sw_list = true; -} - -static void asl_qset_insert(struct whc *whc, struct whc_qset *qset) -{ - struct whc_qset *next, *prev; - - qset_clear(whc, qset); - - /* Link into ASL. */ - qset_get_next_prev(whc, qset, &next, &prev); - whc_qset_set_link_ptr(&qset->qh.link, next->qset_dma); - whc_qset_set_link_ptr(&prev->qh.link, qset->qset_dma); - qset->in_hw_list = true; -} - -static void asl_qset_remove(struct whc *whc, struct whc_qset *qset) -{ - struct whc_qset *prev, *next; - - qset_get_next_prev(whc, qset, &next, &prev); - - list_move(&qset->list_node, &whc->async_removed_list); - qset->in_sw_list = false; - - /* - * No more qsets in the ASL? The caller must stop the ASL as - * it's no longer valid. - */ - if (list_empty(&whc->async_list)) - return; - - /* Remove from ASL. */ - whc_qset_set_link_ptr(&prev->qh.link, next->qset_dma); - qset->in_hw_list = false; -} - -/** - * process_qset - process any recently inactivated or halted qTDs in a - * qset. - * - * After inactive qTDs are removed, new qTDs can be added if the - * urb queue still contains URBs. - * - * Returns any additional WUSBCMD bits for the ASL sync command (i.e., - * WUSBCMD_ASYNC_QSET_RM if a halted qset was removed). - */ -static uint32_t process_qset(struct whc *whc, struct whc_qset *qset) -{ - enum whc_update update = 0; - uint32_t status = 0; - - while (qset->ntds) { - struct whc_qtd *td; - - td = &qset->qtd[qset->td_start]; - status = le32_to_cpu(td->status); - - /* - * Nothing to do with a still active qTD. - */ - if (status & QTD_STS_ACTIVE) - break; - - if (status & QTD_STS_HALTED) { - /* Ug, an error. */ - process_halted_qtd(whc, qset, td); - /* A halted qTD always triggers an update - because the qset was either removed or - reactivated. */ - update |= WHC_UPDATE_UPDATED; - goto done; - } - - /* Mmm, a completed qTD. */ - process_inactive_qtd(whc, qset, td); - } - - if (!qset->remove) - update |= qset_add_qtds(whc, qset); - -done: - /* - * Remove this qset from the ASL if requested, but only if has - * no qTDs. - */ - if (qset->remove && qset->ntds == 0) { - asl_qset_remove(whc, qset); - update |= WHC_UPDATE_REMOVED; - } - return update; -} - -void asl_start(struct whc *whc) -{ - struct whc_qset *qset; - - qset = list_first_entry(&whc->async_list, struct whc_qset, list_node); - - le_writeq(qset->qset_dma | QH_LINK_NTDS(8), whc->base + WUSBASYNCLISTADDR); - - whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, WUSBCMD_ASYNC_EN); - whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, - WUSBSTS_ASYNC_SCHED, WUSBSTS_ASYNC_SCHED, - 1000, "start ASL"); -} - -void asl_stop(struct whc *whc) -{ - whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, 0); - whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, - WUSBSTS_ASYNC_SCHED, 0, - 1000, "stop ASL"); -} - -/** - * asl_update - request an ASL update and wait for the hardware to be synced - * @whc: the WHCI HC - * @wusbcmd: WUSBCMD value to start the update. - * - * If the WUSB HC is inactive (i.e., the ASL is stopped) then the - * update must be skipped as the hardware may not respond to update - * requests. - */ -void asl_update(struct whc *whc, uint32_t wusbcmd) -{ - struct wusbhc *wusbhc = &whc->wusbhc; - long t; - - mutex_lock(&wusbhc->mutex); - if (wusbhc->active) { - whc_write_wusbcmd(whc, wusbcmd, wusbcmd); - t = wait_event_timeout( - whc->async_list_wq, - (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0, - msecs_to_jiffies(1000)); - if (t == 0) - whc_hw_error(whc, "ASL update timeout"); - } - mutex_unlock(&wusbhc->mutex); -} - -/** - * scan_async_work - scan the ASL for qsets to process. - * - * Process each qset in the ASL in turn and then signal the WHC that - * the ASL has been updated. - * - * Then start, stop or update the asynchronous schedule as required. - */ -void scan_async_work(struct work_struct *work) -{ - struct whc *whc = container_of(work, struct whc, async_work); - struct whc_qset *qset, *t; - enum whc_update update = 0; - - spin_lock_irq(&whc->lock); - - /* - * Transerve the software list backwards so new qsets can be - * safely inserted into the ASL without making it non-circular. - */ - list_for_each_entry_safe_reverse(qset, t, &whc->async_list, list_node) { - if (!qset->in_hw_list) { - asl_qset_insert(whc, qset); - update |= WHC_UPDATE_ADDED; - } - - update |= process_qset(whc, qset); - } - - spin_unlock_irq(&whc->lock); - - if (update) { - uint32_t wusbcmd = WUSBCMD_ASYNC_UPDATED | WUSBCMD_ASYNC_SYNCED_DB; - if (update & WHC_UPDATE_REMOVED) - wusbcmd |= WUSBCMD_ASYNC_QSET_RM; - asl_update(whc, wusbcmd); - } - - /* - * Now that the ASL is updated, complete the removal of any - * removed qsets. - * - * If the qset was to be reset, do so and reinsert it into the - * ASL if it has pending transfers. - */ - spin_lock_irq(&whc->lock); - - list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { - qset_remove_complete(whc, qset); - if (qset->reset) { - qset_reset(whc, qset); - if (!list_empty(&qset->stds)) { - asl_qset_insert_begin(whc, qset); - queue_work(whc->workqueue, &whc->async_work); - } - } - } - - spin_unlock_irq(&whc->lock); -} - -/** - * asl_urb_enqueue - queue an URB onto the asynchronous list (ASL). - * @whc: the WHCI host controller - * @urb: the URB to enqueue - * @mem_flags: flags for any memory allocations - * - * The qset for the endpoint is obtained and the urb queued on to it. - * - * Work is scheduled to update the hardware's view of the ASL. - */ -int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) -{ - struct whc_qset *qset; - int err; - unsigned long flags; - - spin_lock_irqsave(&whc->lock, flags); - - err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb); - if (err < 0) { - spin_unlock_irqrestore(&whc->lock, flags); - return err; - } - - qset = get_qset(whc, urb, GFP_ATOMIC); - if (qset == NULL) - err = -ENOMEM; - else - err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); - if (!err) { - if (!qset->in_sw_list && !qset->remove) - asl_qset_insert_begin(whc, qset); - } else - usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); - - spin_unlock_irqrestore(&whc->lock, flags); - - if (!err) - queue_work(whc->workqueue, &whc->async_work); - - return err; -} - -/** - * asl_urb_dequeue - remove an URB (qset) from the async list. - * @whc: the WHCI host controller - * @urb: the URB to dequeue - * @status: the current status of the URB - * - * URBs that do yet have qTDs can simply be removed from the software - * queue, otherwise the qset must be removed from the ASL so the qTDs - * can be removed. - */ -int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status) -{ - struct whc_urb *wurb = urb->hcpriv; - struct whc_qset *qset = wurb->qset; - struct whc_std *std, *t; - bool has_qtd = false; - int ret; - unsigned long flags; - - spin_lock_irqsave(&whc->lock, flags); - - ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status); - if (ret < 0) - goto out; - - list_for_each_entry_safe(std, t, &qset->stds, list_node) { - if (std->urb == urb) { - if (std->qtd) - has_qtd = true; - qset_free_std(whc, std); - } else - std->qtd = NULL; /* so this std is re-added when the qset is */ - } - - if (has_qtd) { - asl_qset_remove(whc, qset); - wurb->status = status; - wurb->is_async = true; - queue_work(whc->workqueue, &wurb->dequeue_work); - } else - qset_remove_urb(whc, qset, urb, status); -out: - spin_unlock_irqrestore(&whc->lock, flags); - - return ret; -} - -/** - * asl_qset_delete - delete a qset from the ASL - */ -void asl_qset_delete(struct whc *whc, struct whc_qset *qset) -{ - qset->remove = 1; - queue_work(whc->workqueue, &whc->async_work); - qset_delete(whc, qset); -} - -/** - * asl_init - initialize the asynchronous schedule list - * - * A dummy qset with no qTDs is added to the ASL to simplify removing - * qsets (no need to stop the ASL when the last qset is removed). - */ -int asl_init(struct whc *whc) -{ - struct whc_qset *qset; - - qset = qset_alloc(whc, GFP_KERNEL); - if (qset == NULL) - return -ENOMEM; - - asl_qset_insert_begin(whc, qset); - asl_qset_insert(whc, qset); - - return 0; -} - -/** - * asl_clean_up - free ASL resources - * - * The ASL is stopped and empty except for the dummy qset. - */ -void asl_clean_up(struct whc *whc) -{ - struct whc_qset *qset; - - if (!list_empty(&whc->async_list)) { - qset = list_first_entry(&whc->async_list, struct whc_qset, list_node); - list_del(&qset->list_node); - qset_free(whc, qset); - } -} diff --git a/drivers/staging/wusbcore/host/whci/debug.c b/drivers/staging/wusbcore/host/whci/debug.c deleted file mode 100644 index 443da6719147..000000000000 --- a/drivers/staging/wusbcore/host/whci/debug.c +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) debug. - * - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include -#include - -#include "../../wusbhc.h" - -#include "whcd.h" - -struct whc_dbg { - struct dentry *di_f; - struct dentry *asl_f; - struct dentry *pzl_f; -}; - -static void qset_print(struct seq_file *s, struct whc_qset *qset) -{ - static const char *qh_type[] = { - "ctrl", "isoc", "bulk", "intr", "rsvd", "rsvd", "rsvd", "lpintr", }; - struct whc_std *std; - struct urb *urb = NULL; - int i; - - seq_printf(s, "qset %08x", (u32)qset->qset_dma); - if (&qset->list_node == qset->whc->async_list.prev) { - seq_printf(s, " (dummy)\n"); - } else { - seq_printf(s, " ep%d%s-%s maxpkt: %d\n", - qset->qh.info1 & 0x0f, - (qset->qh.info1 >> 4) & 0x1 ? "in" : "out", - qh_type[(qset->qh.info1 >> 5) & 0x7], - (qset->qh.info1 >> 16) & 0xffff); - } - seq_printf(s, " -> %08x\n", (u32)qset->qh.link); - seq_printf(s, " info: %08x %08x %08x\n", - qset->qh.info1, qset->qh.info2, qset->qh.info3); - seq_printf(s, " sts: %04x errs: %d curwin: %08x\n", - qset->qh.status, qset->qh.err_count, qset->qh.cur_window); - seq_printf(s, " TD: sts: %08x opts: %08x\n", - qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options); - - for (i = 0; i < WHCI_QSET_TD_MAX; i++) { - seq_printf(s, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n", - i == qset->td_start ? 'S' : ' ', - i == qset->td_end ? 'E' : ' ', - i, qset->qtd[i].status, qset->qtd[i].options, - (u32)qset->qtd[i].page_list_ptr); - } - seq_printf(s, " ntds: %d\n", qset->ntds); - list_for_each_entry(std, &qset->stds, list_node) { - if (urb != std->urb) { - urb = std->urb; - seq_printf(s, " urb %p transferred: %d bytes\n", urb, - urb->actual_length); - } - if (std->qtd) - seq_printf(s, " sTD[%td]: %zu bytes @ %08x\n", - std->qtd - &qset->qtd[0], - std->len, std->num_pointers ? - (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr); - else - seq_printf(s, " sTD[-]: %zd bytes @ %08x\n", - std->len, std->num_pointers ? - (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr); - } -} - -static int di_show(struct seq_file *s, void *p) -{ - struct whc *whc = s->private; - int d; - - for (d = 0; d < whc->n_devices; d++) { - struct di_buf_entry *di = &whc->di_buf[d]; - - seq_printf(s, "DI[%d]\n", d); - seq_printf(s, " availability: %*pb\n", - UWB_NUM_MAS, (unsigned long *)di->availability_info); - seq_printf(s, " %c%c key idx: %d dev addr: %d\n", - (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ', - (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ', - (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8, - (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK)); - } - return 0; -} -DEFINE_SHOW_ATTRIBUTE(di); - -static int asl_show(struct seq_file *s, void *p) -{ - struct whc *whc = s->private; - struct whc_qset *qset; - - list_for_each_entry(qset, &whc->async_list, list_node) { - qset_print(s, qset); - } - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(asl); - -static int pzl_show(struct seq_file *s, void *p) -{ - struct whc *whc = s->private; - struct whc_qset *qset; - int period; - - for (period = 0; period < 5; period++) { - seq_printf(s, "Period %d\n", period); - list_for_each_entry(qset, &whc->periodic_list[period], list_node) { - qset_print(s, qset); - } - } - return 0; -} -DEFINE_SHOW_ATTRIBUTE(pzl); - -void whc_dbg_init(struct whc *whc) -{ - if (whc->wusbhc.pal.debugfs_dir == NULL) - return; - - whc->dbg = kzalloc(sizeof(struct whc_dbg), GFP_KERNEL); - if (whc->dbg == NULL) - return; - - whc->dbg->di_f = debugfs_create_file("di", 0444, - whc->wusbhc.pal.debugfs_dir, whc, - &di_fops); - whc->dbg->asl_f = debugfs_create_file("asl", 0444, - whc->wusbhc.pal.debugfs_dir, whc, - &asl_fops); - whc->dbg->pzl_f = debugfs_create_file("pzl", 0444, - whc->wusbhc.pal.debugfs_dir, whc, - &pzl_fops); -} - -void whc_dbg_clean_up(struct whc *whc) -{ - if (whc->dbg) { - debugfs_remove(whc->dbg->pzl_f); - debugfs_remove(whc->dbg->asl_f); - debugfs_remove(whc->dbg->di_f); - kfree(whc->dbg); - } -} diff --git a/drivers/staging/wusbcore/host/whci/hcd.c b/drivers/staging/wusbcore/host/whci/hcd.c deleted file mode 100644 index bee1ff2d35be..000000000000 --- a/drivers/staging/wusbcore/host/whci/hcd.c +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) driver. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include - -#include "../../../uwb/include/umc.h" -#include "../../wusbhc.h" - -#include "whcd.h" - -/* - * One time initialization. - * - * Nothing to do here. - */ -static int whc_reset(struct usb_hcd *usb_hcd) -{ - return 0; -} - -/* - * Start the wireless host controller. - * - * Start device notification. - * - * Put hc into run state, set DNTS parameters. - */ -static int whc_start(struct usb_hcd *usb_hcd) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct whc *whc = wusbhc_to_whc(wusbhc); - u8 bcid; - int ret; - - mutex_lock(&wusbhc->mutex); - - le_writel(WUSBINTR_GEN_CMD_DONE - | WUSBINTR_HOST_ERR - | WUSBINTR_ASYNC_SCHED_SYNCED - | WUSBINTR_DNTS_INT - | WUSBINTR_ERR_INT - | WUSBINTR_INT, - whc->base + WUSBINTR); - - /* set cluster ID */ - bcid = wusb_cluster_id_get(); - ret = whc_set_cluster_id(whc, bcid); - if (ret < 0) - goto out; - wusbhc->cluster_id = bcid; - - /* start HC */ - whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN); - - usb_hcd->uses_new_polling = 1; - set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); - usb_hcd->state = HC_STATE_RUNNING; - -out: - mutex_unlock(&wusbhc->mutex); - return ret; -} - - -/* - * Stop the wireless host controller. - * - * Stop device notification. - * - * Wait for pending transfer to stop? Put hc into stop state? - */ -static void whc_stop(struct usb_hcd *usb_hcd) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct whc *whc = wusbhc_to_whc(wusbhc); - - mutex_lock(&wusbhc->mutex); - - /* stop HC */ - le_writel(0, whc->base + WUSBINTR); - whc_write_wusbcmd(whc, WUSBCMD_RUN, 0); - whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, - WUSBSTS_HCHALTED, WUSBSTS_HCHALTED, - 100, "HC to halt"); - - wusb_cluster_id_put(wusbhc->cluster_id); - - mutex_unlock(&wusbhc->mutex); -} - -static int whc_get_frame_number(struct usb_hcd *usb_hcd) -{ - /* Frame numbers are not applicable to WUSB. */ - return -ENOSYS; -} - - -/* - * Queue an URB to the ASL or PZL - */ -static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, - gfp_t mem_flags) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct whc *whc = wusbhc_to_whc(wusbhc); - int ret; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_INTERRUPT: - ret = pzl_urb_enqueue(whc, urb, mem_flags); - break; - case PIPE_ISOCHRONOUS: - dev_err(&whc->umc->dev, "isochronous transfers unsupported\n"); - ret = -ENOTSUPP; - break; - case PIPE_CONTROL: - case PIPE_BULK: - default: - ret = asl_urb_enqueue(whc, urb, mem_flags); - break; - } - - return ret; -} - -/* - * Remove a queued URB from the ASL or PZL. - */ -static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct whc *whc = wusbhc_to_whc(wusbhc); - int ret; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_INTERRUPT: - ret = pzl_urb_dequeue(whc, urb, status); - break; - case PIPE_ISOCHRONOUS: - ret = -ENOTSUPP; - break; - case PIPE_CONTROL: - case PIPE_BULK: - default: - ret = asl_urb_dequeue(whc, urb, status); - break; - } - - return ret; -} - -/* - * Wait for all URBs to the endpoint to be completed, then delete the - * qset. - */ -static void whc_endpoint_disable(struct usb_hcd *usb_hcd, - struct usb_host_endpoint *ep) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct whc *whc = wusbhc_to_whc(wusbhc); - struct whc_qset *qset; - - qset = ep->hcpriv; - if (qset) { - ep->hcpriv = NULL; - if (usb_endpoint_xfer_bulk(&ep->desc) - || usb_endpoint_xfer_control(&ep->desc)) - asl_qset_delete(whc, qset); - else - pzl_qset_delete(whc, qset); - } -} - -static void whc_endpoint_reset(struct usb_hcd *usb_hcd, - struct usb_host_endpoint *ep) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct whc *whc = wusbhc_to_whc(wusbhc); - struct whc_qset *qset; - unsigned long flags; - - spin_lock_irqsave(&whc->lock, flags); - - qset = ep->hcpriv; - if (qset) { - qset->remove = 1; - qset->reset = 1; - - if (usb_endpoint_xfer_bulk(&ep->desc) - || usb_endpoint_xfer_control(&ep->desc)) - queue_work(whc->workqueue, &whc->async_work); - else - queue_work(whc->workqueue, &whc->periodic_work); - } - - spin_unlock_irqrestore(&whc->lock, flags); -} - - -static const struct hc_driver whc_hc_driver = { - .description = "whci-hcd", - .product_desc = "Wireless host controller", - .hcd_priv_size = sizeof(struct whc) - sizeof(struct usb_hcd), - .irq = whc_int_handler, - .flags = HCD_USB2, - - .reset = whc_reset, - .start = whc_start, - .stop = whc_stop, - .get_frame_number = whc_get_frame_number, - .urb_enqueue = whc_urb_enqueue, - .urb_dequeue = whc_urb_dequeue, - .endpoint_disable = whc_endpoint_disable, - .endpoint_reset = whc_endpoint_reset, - - .hub_status_data = wusbhc_rh_status_data, - .hub_control = wusbhc_rh_control, - .start_port_reset = wusbhc_rh_start_port_reset, -}; - -static int whc_probe(struct umc_dev *umc) -{ - int ret; - struct usb_hcd *usb_hcd; - struct wusbhc *wusbhc; - struct whc *whc; - struct device *dev = &umc->dev; - - usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci"); - if (usb_hcd == NULL) { - dev_err(dev, "unable to create hcd\n"); - return -ENOMEM; - } - - usb_hcd->wireless = 1; - usb_hcd->self.sg_tablesize = 2048; /* somewhat arbitrary */ - - wusbhc = usb_hcd_to_wusbhc(usb_hcd); - whc = wusbhc_to_whc(wusbhc); - whc->umc = umc; - - ret = whc_init(whc); - if (ret) - goto error_whc_init; - - wusbhc->dev = dev; - wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent); - if (!wusbhc->uwb_rc) { - ret = -ENODEV; - dev_err(dev, "cannot get radio controller\n"); - goto error_uwb_rc; - } - - if (whc->n_devices > USB_MAXCHILDREN) { - dev_warn(dev, "USB_MAXCHILDREN too low for WUSB adapter (%u ports)\n", - whc->n_devices); - wusbhc->ports_max = USB_MAXCHILDREN; - } else - wusbhc->ports_max = whc->n_devices; - wusbhc->mmcies_max = whc->n_mmc_ies; - wusbhc->start = whc_wusbhc_start; - wusbhc->stop = whc_wusbhc_stop; - wusbhc->mmcie_add = whc_mmcie_add; - wusbhc->mmcie_rm = whc_mmcie_rm; - wusbhc->dev_info_set = whc_dev_info_set; - wusbhc->bwa_set = whc_bwa_set; - wusbhc->set_num_dnts = whc_set_num_dnts; - wusbhc->set_ptk = whc_set_ptk; - wusbhc->set_gtk = whc_set_gtk; - - ret = wusbhc_create(wusbhc); - if (ret) - goto error_wusbhc_create; - - ret = usb_add_hcd(usb_hcd, whc->umc->irq, IRQF_SHARED); - if (ret) { - dev_err(dev, "cannot add HCD: %d\n", ret); - goto error_usb_add_hcd; - } - device_wakeup_enable(usb_hcd->self.controller); - - ret = wusbhc_b_create(wusbhc); - if (ret) { - dev_err(dev, "WUSBHC phase B setup failed: %d\n", ret); - goto error_wusbhc_b_create; - } - - whc_dbg_init(whc); - - return 0; - -error_wusbhc_b_create: - usb_remove_hcd(usb_hcd); -error_usb_add_hcd: - wusbhc_destroy(wusbhc); -error_wusbhc_create: - uwb_rc_put(wusbhc->uwb_rc); -error_uwb_rc: - whc_clean_up(whc); -error_whc_init: - usb_put_hcd(usb_hcd); - return ret; -} - - -static void whc_remove(struct umc_dev *umc) -{ - struct usb_hcd *usb_hcd = dev_get_drvdata(&umc->dev); - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - struct whc *whc = wusbhc_to_whc(wusbhc); - - if (usb_hcd) { - whc_dbg_clean_up(whc); - wusbhc_b_destroy(wusbhc); - usb_remove_hcd(usb_hcd); - wusbhc_destroy(wusbhc); - uwb_rc_put(wusbhc->uwb_rc); - whc_clean_up(whc); - usb_put_hcd(usb_hcd); - } -} - -static struct umc_driver whci_hc_driver = { - .name = "whci-hcd", - .cap_id = UMC_CAP_ID_WHCI_WUSB_HC, - .probe = whc_probe, - .remove = whc_remove, -}; - -static int __init whci_hc_driver_init(void) -{ - return umc_driver_register(&whci_hc_driver); -} -module_init(whci_hc_driver_init); - -static void __exit whci_hc_driver_exit(void) -{ - umc_driver_unregister(&whci_hc_driver); -} -module_exit(whci_hc_driver_exit); - -/* PCI device ID's that we handle (so it gets loaded) */ -static struct pci_device_id __used whci_hcd_id_table[] = { - { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) }, - { /* empty last entry */ } -}; -MODULE_DEVICE_TABLE(pci, whci_hcd_id_table); - -MODULE_DESCRIPTION("WHCI Wireless USB host controller driver"); -MODULE_AUTHOR("Cambridge Silicon Radio Ltd."); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/wusbcore/host/whci/hw.c b/drivers/staging/wusbcore/host/whci/hw.c deleted file mode 100644 index e4e8914abf42..000000000000 --- a/drivers/staging/wusbcore/host/whci/hw.c +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) hardware access helpers. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include - -#include "../../../uwb/include/umc.h" -#include "../../wusbhc.h" - -#include "whcd.h" - -void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val) -{ - unsigned long flags; - u32 cmd; - - spin_lock_irqsave(&whc->lock, flags); - - cmd = le_readl(whc->base + WUSBCMD); - cmd = (cmd & ~mask) | val; - le_writel(cmd, whc->base + WUSBCMD); - - spin_unlock_irqrestore(&whc->lock, flags); -} - -/** - * whc_do_gencmd - start a generic command via the WUSBGENCMDSTS register - * @whc: the WHCI HC - * @cmd: command to start. - * @params: parameters for the command (the WUSBGENCMDPARAMS register value). - * @addr: pointer to any data for the command (may be NULL). - * @len: length of the data (if any). - */ -int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len) -{ - unsigned long flags; - dma_addr_t dma_addr; - int t; - int ret = 0; - - mutex_lock(&whc->mutex); - - /* Wait for previous command to complete. */ - t = wait_event_timeout(whc->cmd_wq, - (le_readl(whc->base + WUSBGENCMDSTS) & WUSBGENCMDSTS_ACTIVE) == 0, - WHC_GENCMD_TIMEOUT_MS); - if (t == 0) { - dev_err(&whc->umc->dev, "generic command timeout (%04x/%04x)\n", - le_readl(whc->base + WUSBGENCMDSTS), - le_readl(whc->base + WUSBGENCMDPARAMS)); - ret = -ETIMEDOUT; - goto out; - } - - if (addr) { - memcpy(whc->gen_cmd_buf, addr, len); - dma_addr = whc->gen_cmd_buf_dma; - } else - dma_addr = 0; - - /* Poke registers to start cmd. */ - spin_lock_irqsave(&whc->lock, flags); - - le_writel(params, whc->base + WUSBGENCMDPARAMS); - le_writeq(dma_addr, whc->base + WUSBGENADDR); - - le_writel(WUSBGENCMDSTS_ACTIVE | WUSBGENCMDSTS_IOC | cmd, - whc->base + WUSBGENCMDSTS); - - spin_unlock_irqrestore(&whc->lock, flags); -out: - mutex_unlock(&whc->mutex); - - return ret; -} - -/** - * whc_hw_error - recover from a hardware error - * @whc: the WHCI HC that broke. - * @reason: a description of the failure. - * - * Recover from broken hardware with a full reset. - */ -void whc_hw_error(struct whc *whc, const char *reason) -{ - struct wusbhc *wusbhc = &whc->wusbhc; - - dev_err(&whc->umc->dev, "hardware error: %s\n", reason); - wusbhc_reset_all(wusbhc); -} diff --git a/drivers/staging/wusbcore/host/whci/init.c b/drivers/staging/wusbcore/host/whci/init.c deleted file mode 100644 index 55fd458a8f30..000000000000 --- a/drivers/staging/wusbcore/host/whci/init.c +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) initialization. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include - -#include "../../../uwb/include/umc.h" -#include "../../wusbhc.h" - -#include "whcd.h" - -/* - * Reset the host controller. - */ -static void whc_hw_reset(struct whc *whc) -{ - le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD); - whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0, - 100, "reset"); -} - -static void whc_hw_init_di_buf(struct whc *whc) -{ - int d; - - /* Disable all entries in the Device Information buffer. */ - for (d = 0; d < whc->n_devices; d++) - whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE; - - le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR); -} - -static void whc_hw_init_dn_buf(struct whc *whc) -{ - /* Clear the Device Notification buffer to ensure the V (valid) - * bits are clear. */ - memset(whc->dn_buf, 0, 4096); - - le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR); -} - -int whc_init(struct whc *whc) -{ - u32 whcsparams; - int ret, i; - resource_size_t start, len; - - spin_lock_init(&whc->lock); - mutex_init(&whc->mutex); - init_waitqueue_head(&whc->cmd_wq); - init_waitqueue_head(&whc->async_list_wq); - init_waitqueue_head(&whc->periodic_list_wq); - whc->workqueue = alloc_ordered_workqueue(dev_name(&whc->umc->dev), 0); - if (whc->workqueue == NULL) { - ret = -ENOMEM; - goto error; - } - INIT_WORK(&whc->dn_work, whc_dn_work); - - INIT_WORK(&whc->async_work, scan_async_work); - INIT_LIST_HEAD(&whc->async_list); - INIT_LIST_HEAD(&whc->async_removed_list); - - INIT_WORK(&whc->periodic_work, scan_periodic_work); - for (i = 0; i < 5; i++) - INIT_LIST_HEAD(&whc->periodic_list[i]); - INIT_LIST_HEAD(&whc->periodic_removed_list); - - /* Map HC registers. */ - start = whc->umc->resource.start; - len = whc->umc->resource.end - start + 1; - if (!request_mem_region(start, len, "whci-hc")) { - dev_err(&whc->umc->dev, "can't request HC region\n"); - ret = -EBUSY; - goto error; - } - whc->base_phys = start; - whc->base = ioremap(start, len); - if (!whc->base) { - dev_err(&whc->umc->dev, "ioremap\n"); - ret = -ENOMEM; - goto error; - } - - whc_hw_reset(whc); - - /* Read maximum number of devices, keys and MMC IEs. */ - whcsparams = le_readl(whc->base + WHCSPARAMS); - whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams); - whc->n_keys = WHCSPARAMS_TO_N_KEYS(whcsparams); - whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams); - - dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n", - whc->n_devices, whc->n_keys, whc->n_mmc_ies); - - whc->qset_pool = dma_pool_create("qset", &whc->umc->dev, - sizeof(struct whc_qset), 64, 0); - if (whc->qset_pool == NULL) { - ret = -ENOMEM; - goto error; - } - - ret = asl_init(whc); - if (ret < 0) - goto error; - ret = pzl_init(whc); - if (ret < 0) - goto error; - - /* Allocate and initialize a buffer for generic commands, the - Device Information buffer, and the Device Notification - buffer. */ - - whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN, - &whc->gen_cmd_buf_dma, GFP_KERNEL); - if (whc->gen_cmd_buf == NULL) { - ret = -ENOMEM; - goto error; - } - - whc->dn_buf = dma_alloc_coherent(&whc->umc->dev, - sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES, - &whc->dn_buf_dma, GFP_KERNEL); - if (!whc->dn_buf) { - ret = -ENOMEM; - goto error; - } - whc_hw_init_dn_buf(whc); - - whc->di_buf = dma_alloc_coherent(&whc->umc->dev, - sizeof(struct di_buf_entry) * whc->n_devices, - &whc->di_buf_dma, GFP_KERNEL); - if (!whc->di_buf) { - ret = -ENOMEM; - goto error; - } - whc_hw_init_di_buf(whc); - - return 0; - -error: - whc_clean_up(whc); - return ret; -} - -void whc_clean_up(struct whc *whc) -{ - resource_size_t len; - - if (whc->di_buf) - dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices, - whc->di_buf, whc->di_buf_dma); - if (whc->dn_buf) - dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES, - whc->dn_buf, whc->dn_buf_dma); - if (whc->gen_cmd_buf) - dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN, - whc->gen_cmd_buf, whc->gen_cmd_buf_dma); - - pzl_clean_up(whc); - asl_clean_up(whc); - - dma_pool_destroy(whc->qset_pool); - - len = resource_size(&whc->umc->resource); - if (whc->base) - iounmap(whc->base); - if (whc->base_phys) - release_mem_region(whc->base_phys, len); - - if (whc->workqueue) - destroy_workqueue(whc->workqueue); -} diff --git a/drivers/staging/wusbcore/host/whci/int.c b/drivers/staging/wusbcore/host/whci/int.c deleted file mode 100644 index bdbe35e9366f..000000000000 --- a/drivers/staging/wusbcore/host/whci/int.c +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) interrupt handling. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include - -#include "../../../uwb/include/umc.h" -#include "../../wusbhc.h" - -#include "whcd.h" - -static void transfer_done(struct whc *whc) -{ - queue_work(whc->workqueue, &whc->async_work); - queue_work(whc->workqueue, &whc->periodic_work); -} - -irqreturn_t whc_int_handler(struct usb_hcd *hcd) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(hcd); - struct whc *whc = wusbhc_to_whc(wusbhc); - u32 sts; - - sts = le_readl(whc->base + WUSBSTS); - if (!(sts & WUSBSTS_INT_MASK)) - return IRQ_NONE; - le_writel(sts & WUSBSTS_INT_MASK, whc->base + WUSBSTS); - - if (sts & WUSBSTS_GEN_CMD_DONE) - wake_up(&whc->cmd_wq); - - if (sts & WUSBSTS_HOST_ERR) - dev_err(&whc->umc->dev, "FIXME: host system error\n"); - - if (sts & WUSBSTS_ASYNC_SCHED_SYNCED) - wake_up(&whc->async_list_wq); - - if (sts & WUSBSTS_PERIODIC_SCHED_SYNCED) - wake_up(&whc->periodic_list_wq); - - if (sts & WUSBSTS_DNTS_INT) - queue_work(whc->workqueue, &whc->dn_work); - - /* - * A transfer completed (see [WHCI] section 4.7.1.2 for when - * this occurs). - */ - if (sts & (WUSBSTS_INT | WUSBSTS_ERR_INT)) - transfer_done(whc); - - return IRQ_HANDLED; -} - -static int process_dn_buf(struct whc *whc) -{ - struct wusbhc *wusbhc = &whc->wusbhc; - struct dn_buf_entry *dn; - int processed = 0; - - for (dn = whc->dn_buf; dn < whc->dn_buf + WHC_N_DN_ENTRIES; dn++) { - if (dn->status & WHC_DN_STATUS_VALID) { - wusbhc_handle_dn(wusbhc, dn->src_addr, - (struct wusb_dn_hdr *)dn->dn_data, - dn->msg_size); - dn->status &= ~WHC_DN_STATUS_VALID; - processed++; - } - } - return processed; -} - -void whc_dn_work(struct work_struct *work) -{ - struct whc *whc = container_of(work, struct whc, dn_work); - int processed; - - do { - processed = process_dn_buf(whc); - } while (processed); -} diff --git a/drivers/staging/wusbcore/host/whci/pzl.c b/drivers/staging/wusbcore/host/whci/pzl.c deleted file mode 100644 index 6dfc075f5798..000000000000 --- a/drivers/staging/wusbcore/host/whci/pzl.c +++ /dev/null @@ -1,404 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) periodic schedule management. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include - -#include "../../../uwb/include/umc.h" -#include "../../wusbhc.h" - -#include "whcd.h" - -static void update_pzl_pointers(struct whc *whc, int period, u64 addr) -{ - switch (period) { - case 0: - whc_qset_set_link_ptr(&whc->pz_list[0], addr); - whc_qset_set_link_ptr(&whc->pz_list[2], addr); - whc_qset_set_link_ptr(&whc->pz_list[4], addr); - whc_qset_set_link_ptr(&whc->pz_list[6], addr); - whc_qset_set_link_ptr(&whc->pz_list[8], addr); - whc_qset_set_link_ptr(&whc->pz_list[10], addr); - whc_qset_set_link_ptr(&whc->pz_list[12], addr); - whc_qset_set_link_ptr(&whc->pz_list[14], addr); - break; - case 1: - whc_qset_set_link_ptr(&whc->pz_list[1], addr); - whc_qset_set_link_ptr(&whc->pz_list[5], addr); - whc_qset_set_link_ptr(&whc->pz_list[9], addr); - whc_qset_set_link_ptr(&whc->pz_list[13], addr); - break; - case 2: - whc_qset_set_link_ptr(&whc->pz_list[3], addr); - whc_qset_set_link_ptr(&whc->pz_list[11], addr); - break; - case 3: - whc_qset_set_link_ptr(&whc->pz_list[7], addr); - break; - case 4: - whc_qset_set_link_ptr(&whc->pz_list[15], addr); - break; - } -} - -/* - * Return the 'period' to use for this qset. The minimum interval for - * the endpoint is used so whatever urbs are submitted the device is - * polled often enough. - */ -static int qset_get_period(struct whc *whc, struct whc_qset *qset) -{ - uint8_t bInterval = qset->ep->desc.bInterval; - - if (bInterval < 6) - bInterval = 6; - if (bInterval > 10) - bInterval = 10; - return bInterval - 6; -} - -static void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset) -{ - int period; - - period = qset_get_period(whc, qset); - - qset_clear(whc, qset); - list_move(&qset->list_node, &whc->periodic_list[period]); - qset->in_sw_list = true; -} - -static void pzl_qset_remove(struct whc *whc, struct whc_qset *qset) -{ - list_move(&qset->list_node, &whc->periodic_removed_list); - qset->in_hw_list = false; - qset->in_sw_list = false; -} - -/** - * pzl_process_qset - process any recently inactivated or halted qTDs - * in a qset. - * - * After inactive qTDs are removed, new qTDs can be added if the - * urb queue still contains URBs. - * - * Returns the schedule updates required. - */ -static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset) -{ - enum whc_update update = 0; - uint32_t status = 0; - - while (qset->ntds) { - struct whc_qtd *td; - - td = &qset->qtd[qset->td_start]; - status = le32_to_cpu(td->status); - - /* - * Nothing to do with a still active qTD. - */ - if (status & QTD_STS_ACTIVE) - break; - - if (status & QTD_STS_HALTED) { - /* Ug, an error. */ - process_halted_qtd(whc, qset, td); - /* A halted qTD always triggers an update - because the qset was either removed or - reactivated. */ - update |= WHC_UPDATE_UPDATED; - goto done; - } - - /* Mmm, a completed qTD. */ - process_inactive_qtd(whc, qset, td); - } - - if (!qset->remove) - update |= qset_add_qtds(whc, qset); - -done: - /* - * If there are no qTDs in this qset, remove it from the PZL. - */ - if (qset->remove && qset->ntds == 0) { - pzl_qset_remove(whc, qset); - update |= WHC_UPDATE_REMOVED; - } - - return update; -} - -/** - * pzl_start - start the periodic schedule - * @whc: the WHCI host controller - * - * The PZL must be valid (e.g., all entries in the list should have - * the T bit set). - */ -void pzl_start(struct whc *whc) -{ - le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); - - whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN); - whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, - WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED, - 1000, "start PZL"); -} - -/** - * pzl_stop - stop the periodic schedule - * @whc: the WHCI host controller - */ -void pzl_stop(struct whc *whc) -{ - whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0); - whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, - WUSBSTS_PERIODIC_SCHED, 0, - 1000, "stop PZL"); -} - -/** - * pzl_update - request a PZL update and wait for the hardware to be synced - * @whc: the WHCI HC - * @wusbcmd: WUSBCMD value to start the update. - * - * If the WUSB HC is inactive (i.e., the PZL is stopped) then the - * update must be skipped as the hardware may not respond to update - * requests. - */ -void pzl_update(struct whc *whc, uint32_t wusbcmd) -{ - struct wusbhc *wusbhc = &whc->wusbhc; - long t; - - mutex_lock(&wusbhc->mutex); - if (wusbhc->active) { - whc_write_wusbcmd(whc, wusbcmd, wusbcmd); - t = wait_event_timeout( - whc->periodic_list_wq, - (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0, - msecs_to_jiffies(1000)); - if (t == 0) - whc_hw_error(whc, "PZL update timeout"); - } - mutex_unlock(&wusbhc->mutex); -} - -static void update_pzl_hw_view(struct whc *whc) -{ - struct whc_qset *qset, *t; - int period; - u64 tmp_qh = 0; - - for (period = 0; period < 5; period++) { - list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { - whc_qset_set_link_ptr(&qset->qh.link, tmp_qh); - tmp_qh = qset->qset_dma; - qset->in_hw_list = true; - } - update_pzl_pointers(whc, period, tmp_qh); - } -} - -/** - * scan_periodic_work - scan the PZL for qsets to process. - * - * Process each qset in the PZL in turn and then signal the WHC that - * the PZL has been updated. - * - * Then start, stop or update the periodic schedule as required. - */ -void scan_periodic_work(struct work_struct *work) -{ - struct whc *whc = container_of(work, struct whc, periodic_work); - struct whc_qset *qset, *t; - enum whc_update update = 0; - int period; - - spin_lock_irq(&whc->lock); - - for (period = 4; period >= 0; period--) { - list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { - if (!qset->in_hw_list) - update |= WHC_UPDATE_ADDED; - update |= pzl_process_qset(whc, qset); - } - } - - if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED)) - update_pzl_hw_view(whc); - - spin_unlock_irq(&whc->lock); - - if (update) { - uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB; - if (update & WHC_UPDATE_REMOVED) - wusbcmd |= WUSBCMD_PERIODIC_QSET_RM; - pzl_update(whc, wusbcmd); - } - - /* - * Now that the PZL is updated, complete the removal of any - * removed qsets. - * - * If the qset was to be reset, do so and reinsert it into the - * PZL if it has pending transfers. - */ - spin_lock_irq(&whc->lock); - - list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { - qset_remove_complete(whc, qset); - if (qset->reset) { - qset_reset(whc, qset); - if (!list_empty(&qset->stds)) { - qset_insert_in_sw_list(whc, qset); - queue_work(whc->workqueue, &whc->periodic_work); - } - } - } - - spin_unlock_irq(&whc->lock); -} - -/** - * pzl_urb_enqueue - queue an URB onto the periodic list (PZL) - * @whc: the WHCI host controller - * @urb: the URB to enqueue - * @mem_flags: flags for any memory allocations - * - * The qset for the endpoint is obtained and the urb queued on to it. - * - * Work is scheduled to update the hardware's view of the PZL. - */ -int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) -{ - struct whc_qset *qset; - int err; - unsigned long flags; - - spin_lock_irqsave(&whc->lock, flags); - - err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb); - if (err < 0) { - spin_unlock_irqrestore(&whc->lock, flags); - return err; - } - - qset = get_qset(whc, urb, GFP_ATOMIC); - if (qset == NULL) - err = -ENOMEM; - else - err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); - if (!err) { - if (!qset->in_sw_list && !qset->remove) - qset_insert_in_sw_list(whc, qset); - } else - usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); - - spin_unlock_irqrestore(&whc->lock, flags); - - if (!err) - queue_work(whc->workqueue, &whc->periodic_work); - - return err; -} - -/** - * pzl_urb_dequeue - remove an URB (qset) from the periodic list - * @whc: the WHCI host controller - * @urb: the URB to dequeue - * @status: the current status of the URB - * - * URBs that do yet have qTDs can simply be removed from the software - * queue, otherwise the qset must be removed so the qTDs can be safely - * removed. - */ -int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status) -{ - struct whc_urb *wurb = urb->hcpriv; - struct whc_qset *qset = wurb->qset; - struct whc_std *std, *t; - bool has_qtd = false; - int ret; - unsigned long flags; - - spin_lock_irqsave(&whc->lock, flags); - - ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status); - if (ret < 0) - goto out; - - list_for_each_entry_safe(std, t, &qset->stds, list_node) { - if (std->urb == urb) { - if (std->qtd) - has_qtd = true; - qset_free_std(whc, std); - } else - std->qtd = NULL; /* so this std is re-added when the qset is */ - } - - if (has_qtd) { - pzl_qset_remove(whc, qset); - update_pzl_hw_view(whc); - wurb->status = status; - wurb->is_async = false; - queue_work(whc->workqueue, &wurb->dequeue_work); - } else - qset_remove_urb(whc, qset, urb, status); -out: - spin_unlock_irqrestore(&whc->lock, flags); - - return ret; -} - -/** - * pzl_qset_delete - delete a qset from the PZL - */ -void pzl_qset_delete(struct whc *whc, struct whc_qset *qset) -{ - qset->remove = 1; - queue_work(whc->workqueue, &whc->periodic_work); - qset_delete(whc, qset); -} - -/** - * pzl_init - initialize the periodic zone list - * @whc: the WHCI host controller - */ -int pzl_init(struct whc *whc) -{ - int i; - - whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16, - &whc->pz_list_dma, GFP_KERNEL); - if (whc->pz_list == NULL) - return -ENOMEM; - - /* Set T bit on all elements in PZL. */ - for (i = 0; i < 16; i++) - whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T); - - le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); - - return 0; -} - -/** - * pzl_clean_up - free PZL resources - * @whc: the WHCI host controller - * - * The PZL is stopped and empty. - */ -void pzl_clean_up(struct whc *whc) -{ - if (whc->pz_list) - dma_free_coherent(&whc->umc->dev, sizeof(u64) * 16, whc->pz_list, - whc->pz_list_dma); -} diff --git a/drivers/staging/wusbcore/host/whci/qset.c b/drivers/staging/wusbcore/host/whci/qset.c deleted file mode 100644 index 66459b77dc77..000000000000 --- a/drivers/staging/wusbcore/host/whci/qset.c +++ /dev/null @@ -1,831 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) qset management. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include -#include -#include -#include - -#include "../../../uwb/include/umc.h" -#include "../../wusbhc.h" - -#include "whcd.h" - -struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags) -{ - struct whc_qset *qset; - dma_addr_t dma; - - qset = dma_pool_zalloc(whc->qset_pool, mem_flags, &dma); - if (qset == NULL) - return NULL; - - qset->qset_dma = dma; - qset->whc = whc; - - INIT_LIST_HEAD(&qset->list_node); - INIT_LIST_HEAD(&qset->stds); - - return qset; -} - -/** - * qset_fill_qh - fill the static endpoint state in a qset's QHead - * @qset: the qset whose QH needs initializing with static endpoint - * state - * @urb: an urb for a transfer to this endpoint - */ -static void qset_fill_qh(struct whc *whc, struct whc_qset *qset, struct urb *urb) -{ - struct usb_device *usb_dev = urb->dev; - struct wusb_dev *wusb_dev = usb_dev->wusb_dev; - struct usb_wireless_ep_comp_descriptor *epcd; - bool is_out; - uint8_t phy_rate; - - is_out = usb_pipeout(urb->pipe); - - qset->max_packet = le16_to_cpu(urb->ep->desc.wMaxPacketSize); - - epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; - if (epcd) { - qset->max_seq = epcd->bMaxSequence; - qset->max_burst = epcd->bMaxBurst; - } else { - qset->max_seq = 2; - qset->max_burst = 1; - } - - /* - * Initial PHY rate is 53.3 Mbit/s for control endpoints or - * the maximum supported by the device for other endpoints - * (unless limited by the user). - */ - if (usb_pipecontrol(urb->pipe)) - phy_rate = UWB_PHY_RATE_53; - else { - uint16_t phy_rates; - - phy_rates = le16_to_cpu(wusb_dev->wusb_cap_descr->wPHYRates); - phy_rate = fls(phy_rates) - 1; - if (phy_rate > whc->wusbhc.phy_rate) - phy_rate = whc->wusbhc.phy_rate; - } - - qset->qh.info1 = cpu_to_le32( - QH_INFO1_EP(usb_pipeendpoint(urb->pipe)) - | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN) - | usb_pipe_to_qh_type(urb->pipe) - | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum)) - | QH_INFO1_MAX_PKT_LEN(qset->max_packet) - ); - qset->qh.info2 = cpu_to_le32( - QH_INFO2_BURST(qset->max_burst) - | QH_INFO2_DBP(0) - | QH_INFO2_MAX_COUNT(3) - | QH_INFO2_MAX_RETRY(3) - | QH_INFO2_MAX_SEQ(qset->max_seq - 1) - ); - /* FIXME: where can we obtain these Tx parameters from? Why - * doesn't the chip know what Tx power to use? It knows the Rx - * strength and can presumably guess the Tx power required - * from that? */ - qset->qh.info3 = cpu_to_le32( - QH_INFO3_TX_RATE(phy_rate) - | QH_INFO3_TX_PWR(0) /* 0 == max power */ - ); - - qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); -} - -/** - * qset_clear - clear fields in a qset so it may be reinserted into a - * schedule. - * - * The sequence number and current window are not cleared (see - * qset_reset()). - */ -void qset_clear(struct whc *whc, struct whc_qset *qset) -{ - qset->td_start = qset->td_end = qset->ntds = 0; - - qset->qh.link = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T); - qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK; - qset->qh.err_count = 0; - qset->qh.scratch[0] = 0; - qset->qh.scratch[1] = 0; - qset->qh.scratch[2] = 0; - - memset(&qset->qh.overlay, 0, sizeof(qset->qh.overlay)); - - init_completion(&qset->remove_complete); -} - -/** - * qset_reset - reset endpoint state in a qset. - * - * Clears the sequence number and current window. This qset must not - * be in the ASL or PZL. - */ -void qset_reset(struct whc *whc, struct whc_qset *qset) -{ - qset->reset = 0; - - qset->qh.status &= ~QH_STATUS_SEQ_MASK; - qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); -} - -/** - * get_qset - get the qset for an async endpoint - * - * A new qset is created if one does not already exist. - */ -struct whc_qset *get_qset(struct whc *whc, struct urb *urb, - gfp_t mem_flags) -{ - struct whc_qset *qset; - - qset = urb->ep->hcpriv; - if (qset == NULL) { - qset = qset_alloc(whc, mem_flags); - if (qset == NULL) - return NULL; - - qset->ep = urb->ep; - urb->ep->hcpriv = qset; - qset_fill_qh(whc, qset, urb); - } - return qset; -} - -void qset_remove_complete(struct whc *whc, struct whc_qset *qset) -{ - qset->remove = 0; - list_del_init(&qset->list_node); - complete(&qset->remove_complete); -} - -/** - * qset_add_qtds - add qTDs for an URB to a qset - * - * Returns true if the list (ASL/PZL) must be updated because (for a - * WHCI 0.95 controller) an activated qTD was pointed to be iCur. - */ -enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset) -{ - struct whc_std *std; - enum whc_update update = 0; - - list_for_each_entry(std, &qset->stds, list_node) { - struct whc_qtd *qtd; - uint32_t status; - - if (qset->ntds >= WHCI_QSET_TD_MAX - || (qset->pause_after_urb && std->urb != qset->pause_after_urb)) - break; - - if (std->qtd) - continue; /* already has a qTD */ - - qtd = std->qtd = &qset->qtd[qset->td_end]; - - /* Fill in setup bytes for control transfers. */ - if (usb_pipecontrol(std->urb->pipe)) - memcpy(qtd->setup, std->urb->setup_packet, 8); - - status = QTD_STS_ACTIVE | QTD_STS_LEN(std->len); - - if (whc_std_last(std) && usb_pipeout(std->urb->pipe)) - status |= QTD_STS_LAST_PKT; - - /* - * For an IN transfer the iAlt field should be set so - * the h/w will automatically advance to the next - * transfer. However, if there are 8 or more TDs - * remaining in this transfer then iAlt cannot be set - * as it could point to somewhere in this transfer. - */ - if (std->ntds_remaining < WHCI_QSET_TD_MAX) { - int ialt; - ialt = (qset->td_end + std->ntds_remaining) % WHCI_QSET_TD_MAX; - status |= QTD_STS_IALT(ialt); - } else if (usb_pipein(std->urb->pipe)) - qset->pause_after_urb = std->urb; - - if (std->num_pointers) - qtd->options = cpu_to_le32(QTD_OPT_IOC); - else - qtd->options = cpu_to_le32(QTD_OPT_IOC | QTD_OPT_SMALL); - qtd->page_list_ptr = cpu_to_le64(std->dma_addr); - - qtd->status = cpu_to_le32(status); - - if (QH_STATUS_TO_ICUR(qset->qh.status) == qset->td_end) - update = WHC_UPDATE_UPDATED; - - if (++qset->td_end >= WHCI_QSET_TD_MAX) - qset->td_end = 0; - qset->ntds++; - } - - return update; -} - -/** - * qset_remove_qtd - remove the first qTD from a qset. - * - * The qTD might be still active (if it's part of a IN URB that - * resulted in a short read) so ensure it's deactivated. - */ -static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset) -{ - qset->qtd[qset->td_start].status = 0; - - if (++qset->td_start >= WHCI_QSET_TD_MAX) - qset->td_start = 0; - qset->ntds--; -} - -static void qset_copy_bounce_to_sg(struct whc *whc, struct whc_std *std) -{ - struct scatterlist *sg; - void *bounce; - size_t remaining, offset; - - bounce = std->bounce_buf; - remaining = std->len; - - sg = std->bounce_sg; - offset = std->bounce_offset; - - while (remaining) { - size_t len; - - len = min(sg->length - offset, remaining); - memcpy(sg_virt(sg) + offset, bounce, len); - - bounce += len; - remaining -= len; - - offset += len; - if (offset >= sg->length) { - sg = sg_next(sg); - offset = 0; - } - } - -} - -/** - * qset_free_std - remove an sTD and free it. - * @whc: the WHCI host controller - * @std: the sTD to remove and free. - */ -void qset_free_std(struct whc *whc, struct whc_std *std) -{ - list_del(&std->list_node); - if (std->bounce_buf) { - bool is_out = usb_pipeout(std->urb->pipe); - dma_addr_t dma_addr; - - if (std->num_pointers) - dma_addr = le64_to_cpu(std->pl_virt[0].buf_ptr); - else - dma_addr = std->dma_addr; - - dma_unmap_single(whc->wusbhc.dev, dma_addr, - std->len, is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (!is_out) - qset_copy_bounce_to_sg(whc, std); - kfree(std->bounce_buf); - } - if (std->pl_virt) { - if (!dma_mapping_error(whc->wusbhc.dev, std->dma_addr)) - dma_unmap_single(whc->wusbhc.dev, std->dma_addr, - std->num_pointers * sizeof(struct whc_page_list_entry), - DMA_TO_DEVICE); - kfree(std->pl_virt); - std->pl_virt = NULL; - } - kfree(std); -} - -/** - * qset_remove_qtds - remove an URB's qTDs (and sTDs). - */ -static void qset_remove_qtds(struct whc *whc, struct whc_qset *qset, - struct urb *urb) -{ - struct whc_std *std, *t; - - list_for_each_entry_safe(std, t, &qset->stds, list_node) { - if (std->urb != urb) - break; - if (std->qtd != NULL) - qset_remove_qtd(whc, qset); - qset_free_std(whc, std); - } -} - -/** - * qset_free_stds - free any remaining sTDs for an URB. - */ -static void qset_free_stds(struct whc_qset *qset, struct urb *urb) -{ - struct whc_std *std, *t; - - list_for_each_entry_safe(std, t, &qset->stds, list_node) { - if (std->urb == urb) - qset_free_std(qset->whc, std); - } -} - -static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_flags) -{ - dma_addr_t dma_addr = std->dma_addr; - dma_addr_t sp, ep; - size_t pl_len; - int p; - - /* Short buffers don't need a page list. */ - if (std->len <= WHCI_PAGE_SIZE) { - std->num_pointers = 0; - return 0; - } - - sp = dma_addr & ~(WHCI_PAGE_SIZE-1); - ep = dma_addr + std->len; - std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); - - pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); - std->pl_virt = kmalloc(pl_len, mem_flags); - if (std->pl_virt == NULL) - return -ENOMEM; - std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, pl_len, DMA_TO_DEVICE); - if (dma_mapping_error(whc->wusbhc.dev, std->dma_addr)) { - kfree(std->pl_virt); - return -EFAULT; - } - - for (p = 0; p < std->num_pointers; p++) { - std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); - dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); - } - - return 0; -} - -/** - * urb_dequeue_work - executes asl/pzl update and gives back the urb to the system. - */ -static void urb_dequeue_work(struct work_struct *work) -{ - struct whc_urb *wurb = container_of(work, struct whc_urb, dequeue_work); - struct whc_qset *qset = wurb->qset; - struct whc *whc = qset->whc; - unsigned long flags; - - if (wurb->is_async) - asl_update(whc, WUSBCMD_ASYNC_UPDATED - | WUSBCMD_ASYNC_SYNCED_DB - | WUSBCMD_ASYNC_QSET_RM); - else - pzl_update(whc, WUSBCMD_PERIODIC_UPDATED - | WUSBCMD_PERIODIC_SYNCED_DB - | WUSBCMD_PERIODIC_QSET_RM); - - spin_lock_irqsave(&whc->lock, flags); - qset_remove_urb(whc, qset, wurb->urb, wurb->status); - spin_unlock_irqrestore(&whc->lock, flags); -} - -static struct whc_std *qset_new_std(struct whc *whc, struct whc_qset *qset, - struct urb *urb, gfp_t mem_flags) -{ - struct whc_std *std; - - std = kzalloc(sizeof(struct whc_std), mem_flags); - if (std == NULL) - return NULL; - - std->urb = urb; - std->qtd = NULL; - - INIT_LIST_HEAD(&std->list_node); - list_add_tail(&std->list_node, &qset->stds); - - return std; -} - -static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *urb, - gfp_t mem_flags) -{ - size_t remaining; - struct scatterlist *sg; - int i; - int ntds = 0; - struct whc_std *std = NULL; - struct whc_page_list_entry *new_pl_virt; - dma_addr_t prev_end = 0; - size_t pl_len; - int p = 0; - - remaining = urb->transfer_buffer_length; - - for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { - dma_addr_t dma_addr; - size_t dma_remaining; - dma_addr_t sp, ep; - int num_pointers; - - if (remaining == 0) { - break; - } - - dma_addr = sg_dma_address(sg); - dma_remaining = min_t(size_t, sg_dma_len(sg), remaining); - - while (dma_remaining) { - size_t dma_len; - - /* - * We can use the previous std (if it exists) provided that: - * - the previous one ended on a page boundary. - * - the current one begins on a page boundary. - * - the previous one isn't full. - * - * If a new std is needed but the previous one - * was not a whole number of packets then this - * sg list cannot be mapped onto multiple - * qTDs. Return an error and let the caller - * sort it out. - */ - if (!std - || (prev_end & (WHCI_PAGE_SIZE-1)) - || (dma_addr & (WHCI_PAGE_SIZE-1)) - || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { - if (std && std->len % qset->max_packet != 0) - return -EINVAL; - std = qset_new_std(whc, qset, urb, mem_flags); - if (std == NULL) { - return -ENOMEM; - } - ntds++; - p = 0; - } - - dma_len = dma_remaining; - - /* - * If the remainder of this element doesn't - * fit in a single qTD, limit the qTD to a - * whole number of packets. This allows the - * remainder to go into the next qTD. - */ - if (std->len + dma_len > QTD_MAX_XFER_SIZE) { - dma_len = (QTD_MAX_XFER_SIZE / qset->max_packet) - * qset->max_packet - std->len; - } - - std->len += dma_len; - std->ntds_remaining = -1; /* filled in later */ - - sp = dma_addr & ~(WHCI_PAGE_SIZE-1); - ep = dma_addr + dma_len; - num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); - std->num_pointers += num_pointers; - - pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); - - new_pl_virt = krealloc(std->pl_virt, pl_len, mem_flags); - if (new_pl_virt == NULL) { - kfree(std->pl_virt); - std->pl_virt = NULL; - return -ENOMEM; - } - std->pl_virt = new_pl_virt; - - for (;p < std->num_pointers; p++) { - std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); - dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); - } - - prev_end = dma_addr = ep; - dma_remaining -= dma_len; - remaining -= dma_len; - } - } - - /* Now the number of stds is know, go back and fill in - std->ntds_remaining. */ - list_for_each_entry(std, &qset->stds, list_node) { - if (std->ntds_remaining == -1) { - pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); - std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, - pl_len, DMA_TO_DEVICE); - if (dma_mapping_error(whc->wusbhc.dev, std->dma_addr)) - return -EFAULT; - std->ntds_remaining = ntds--; - } - } - return 0; -} - -/** - * qset_add_urb_sg_linearize - add an urb with sg list, copying the data - * - * If the URB contains an sg list whose elements cannot be directly - * mapped to qTDs then the data must be transferred via bounce - * buffers. - */ -static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, - struct urb *urb, gfp_t mem_flags) -{ - bool is_out = usb_pipeout(urb->pipe); - size_t max_std_len; - size_t remaining; - int ntds = 0; - struct whc_std *std = NULL; - void *bounce = NULL; - struct scatterlist *sg; - int i; - - /* limit maximum bounce buffer to 16 * 3.5 KiB ~= 28 k */ - max_std_len = qset->max_burst * qset->max_packet; - - remaining = urb->transfer_buffer_length; - - for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { - size_t len; - size_t sg_remaining; - void *orig; - - if (remaining == 0) { - break; - } - - sg_remaining = min_t(size_t, remaining, sg->length); - orig = sg_virt(sg); - - while (sg_remaining) { - if (!std || std->len == max_std_len) { - std = qset_new_std(whc, qset, urb, mem_flags); - if (std == NULL) - return -ENOMEM; - std->bounce_buf = kmalloc(max_std_len, mem_flags); - if (std->bounce_buf == NULL) - return -ENOMEM; - std->bounce_sg = sg; - std->bounce_offset = orig - sg_virt(sg); - bounce = std->bounce_buf; - ntds++; - } - - len = min(sg_remaining, max_std_len - std->len); - - if (is_out) - memcpy(bounce, orig, len); - - std->len += len; - std->ntds_remaining = -1; /* filled in later */ - - bounce += len; - orig += len; - sg_remaining -= len; - remaining -= len; - } - } - - /* - * For each of the new sTDs, map the bounce buffers, create - * page lists (if necessary), and fill in std->ntds_remaining. - */ - list_for_each_entry(std, &qset->stds, list_node) { - if (std->ntds_remaining != -1) - continue; - - std->dma_addr = dma_map_single(&whc->umc->dev, std->bounce_buf, std->len, - is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(&whc->umc->dev, std->dma_addr)) - return -EFAULT; - - if (qset_fill_page_list(whc, std, mem_flags) < 0) - return -ENOMEM; - - std->ntds_remaining = ntds--; - } - - return 0; -} - -/** - * qset_add_urb - add an urb to the qset's queue. - * - * The URB is chopped into sTDs, one for each qTD that will required. - * At least one qTD (and sTD) is required even if the transfer has no - * data (e.g., for some control transfers). - */ -int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb, - gfp_t mem_flags) -{ - struct whc_urb *wurb; - int remaining = urb->transfer_buffer_length; - u64 transfer_dma = urb->transfer_dma; - int ntds_remaining; - int ret; - - wurb = kzalloc(sizeof(struct whc_urb), mem_flags); - if (wurb == NULL) - goto err_no_mem; - urb->hcpriv = wurb; - wurb->qset = qset; - wurb->urb = urb; - INIT_WORK(&wurb->dequeue_work, urb_dequeue_work); - - if (urb->num_sgs) { - ret = qset_add_urb_sg(whc, qset, urb, mem_flags); - if (ret == -EINVAL) { - qset_free_stds(qset, urb); - ret = qset_add_urb_sg_linearize(whc, qset, urb, mem_flags); - } - if (ret < 0) - goto err_no_mem; - return 0; - } - - ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); - if (ntds_remaining == 0) - ntds_remaining = 1; - - while (ntds_remaining) { - struct whc_std *std; - size_t std_len; - - std_len = remaining; - if (std_len > QTD_MAX_XFER_SIZE) - std_len = QTD_MAX_XFER_SIZE; - - std = qset_new_std(whc, qset, urb, mem_flags); - if (std == NULL) - goto err_no_mem; - - std->dma_addr = transfer_dma; - std->len = std_len; - std->ntds_remaining = ntds_remaining; - - if (qset_fill_page_list(whc, std, mem_flags) < 0) - goto err_no_mem; - - ntds_remaining--; - remaining -= std_len; - transfer_dma += std_len; - } - - return 0; - -err_no_mem: - qset_free_stds(qset, urb); - return -ENOMEM; -} - -/** - * qset_remove_urb - remove an URB from the urb queue. - * - * The URB is returned to the USB subsystem. - */ -void qset_remove_urb(struct whc *whc, struct whc_qset *qset, - struct urb *urb, int status) -{ - struct wusbhc *wusbhc = &whc->wusbhc; - struct whc_urb *wurb = urb->hcpriv; - - usb_hcd_unlink_urb_from_ep(&wusbhc->usb_hcd, urb); - /* Drop the lock as urb->complete() may enqueue another urb. */ - spin_unlock(&whc->lock); - wusbhc_giveback_urb(wusbhc, urb, status); - spin_lock(&whc->lock); - - kfree(wurb); -} - -/** - * get_urb_status_from_qtd - get the completed urb status from qTD status - * @urb: completed urb - * @status: qTD status - */ -static int get_urb_status_from_qtd(struct urb *urb, u32 status) -{ - if (status & QTD_STS_HALTED) { - if (status & QTD_STS_DBE) - return usb_pipein(urb->pipe) ? -ENOSR : -ECOMM; - else if (status & QTD_STS_BABBLE) - return -EOVERFLOW; - else if (status & QTD_STS_RCE) - return -ETIME; - return -EPIPE; - } - if (usb_pipein(urb->pipe) - && (urb->transfer_flags & URB_SHORT_NOT_OK) - && urb->actual_length < urb->transfer_buffer_length) - return -EREMOTEIO; - return 0; -} - -/** - * process_inactive_qtd - process an inactive (but not halted) qTD. - * - * Update the urb with the transfer bytes from the qTD, if the urb is - * completely transferred or (in the case of an IN only) the LPF is - * set, then the transfer is complete and the urb should be returned - * to the system. - */ -void process_inactive_qtd(struct whc *whc, struct whc_qset *qset, - struct whc_qtd *qtd) -{ - struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node); - struct urb *urb = std->urb; - uint32_t status; - bool complete; - - status = le32_to_cpu(qtd->status); - - urb->actual_length += std->len - QTD_STS_TO_LEN(status); - - if (usb_pipein(urb->pipe) && (status & QTD_STS_LAST_PKT)) - complete = true; - else - complete = whc_std_last(std); - - qset_remove_qtd(whc, qset); - qset_free_std(whc, std); - - /* - * Transfers for this URB are complete? Then return it to the - * USB subsystem. - */ - if (complete) { - qset_remove_qtds(whc, qset, urb); - qset_remove_urb(whc, qset, urb, get_urb_status_from_qtd(urb, status)); - - /* - * If iAlt isn't valid then the hardware didn't - * advance iCur. Adjust the start and end pointers to - * match iCur. - */ - if (!(status & QTD_STS_IALT_VALID)) - qset->td_start = qset->td_end - = QH_STATUS_TO_ICUR(le16_to_cpu(qset->qh.status)); - qset->pause_after_urb = NULL; - } -} - -/** - * process_halted_qtd - process a qset with a halted qtd - * - * Remove all the qTDs for the failed URB and return the failed URB to - * the USB subsystem. Then remove all other qTDs so the qset can be - * removed. - * - * FIXME: this is the point where rate adaptation can be done. If a - * transfer failed because it exceeded the maximum number of retries - * then it could be reactivated with a slower rate without having to - * remove the qset. - */ -void process_halted_qtd(struct whc *whc, struct whc_qset *qset, - struct whc_qtd *qtd) -{ - struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node); - struct urb *urb = std->urb; - int urb_status; - - urb_status = get_urb_status_from_qtd(urb, le32_to_cpu(qtd->status)); - - qset_remove_qtds(whc, qset, urb); - qset_remove_urb(whc, qset, urb, urb_status); - - list_for_each_entry(std, &qset->stds, list_node) { - if (qset->ntds == 0) - break; - qset_remove_qtd(whc, qset); - std->qtd = NULL; - } - - qset->remove = 1; -} - -void qset_free(struct whc *whc, struct whc_qset *qset) -{ - dma_pool_free(whc->qset_pool, qset, qset->qset_dma); -} - -/** - * qset_delete - wait for a qset to be unused, then free it. - */ -void qset_delete(struct whc *whc, struct whc_qset *qset) -{ - wait_for_completion(&qset->remove_complete); - qset_free(whc, qset); -} diff --git a/drivers/staging/wusbcore/host/whci/whcd.h b/drivers/staging/wusbcore/host/whci/whcd.h deleted file mode 100644 index a442a2589e83..000000000000 --- a/drivers/staging/wusbcore/host/whci/whcd.h +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) private header. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#ifndef __WHCD_H -#define __WHCD_H - -#include - -#include "../../../uwb/include/whci.h" -#include "../../../uwb/include/umc.h" -#include "whci-hc.h" - -/* Generic command timeout. */ -#define WHC_GENCMD_TIMEOUT_MS 100 - -struct whc_dbg; - -struct whc { - struct wusbhc wusbhc; - struct umc_dev *umc; - - resource_size_t base_phys; - void __iomem *base; - int irq; - - u8 n_devices; - u8 n_keys; - u8 n_mmc_ies; - - u64 *pz_list; - struct dn_buf_entry *dn_buf; - struct di_buf_entry *di_buf; - dma_addr_t pz_list_dma; - dma_addr_t dn_buf_dma; - dma_addr_t di_buf_dma; - - spinlock_t lock; - struct mutex mutex; - - void * gen_cmd_buf; - dma_addr_t gen_cmd_buf_dma; - wait_queue_head_t cmd_wq; - - struct workqueue_struct *workqueue; - struct work_struct dn_work; - - struct dma_pool *qset_pool; - - struct list_head async_list; - struct list_head async_removed_list; - wait_queue_head_t async_list_wq; - struct work_struct async_work; - - struct list_head periodic_list[5]; - struct list_head periodic_removed_list; - wait_queue_head_t periodic_list_wq; - struct work_struct periodic_work; - - struct whc_dbg *dbg; -}; - -#define wusbhc_to_whc(w) (container_of((w), struct whc, wusbhc)) - -/** - * struct whc_std - a software TD. - * @urb: the URB this sTD is for. - * @offset: start of the URB's data for this TD. - * @len: the length of data in the associated TD. - * @ntds_remaining: number of TDs (starting from this one) in this transfer. - * - * @bounce_buf: a bounce buffer if the std was from an urb with a sg - * list that could not be mapped to qTDs directly. - * @bounce_sg: the first scatterlist element bounce_buf is for. - * @bounce_offset: the offset into bounce_sg for the start of bounce_buf. - * - * Queued URBs may require more TDs than are available in a qset so we - * use a list of these "software TDs" (sTDs) to hold per-TD data. - */ -struct whc_std { - struct urb *urb; - size_t len; - int ntds_remaining; - struct whc_qtd *qtd; - - struct list_head list_node; - int num_pointers; - dma_addr_t dma_addr; - struct whc_page_list_entry *pl_virt; - - void *bounce_buf; - struct scatterlist *bounce_sg; - unsigned bounce_offset; -}; - -/** - * struct whc_urb - per URB host controller structure. - * @urb: the URB this struct is for. - * @qset: the qset associated to the URB. - * @dequeue_work: the work to remove the URB when dequeued. - * @is_async: the URB belongs to async sheduler or not. - * @status: the status to be returned when calling wusbhc_giveback_urb. - */ -struct whc_urb { - struct urb *urb; - struct whc_qset *qset; - struct work_struct dequeue_work; - bool is_async; - int status; -}; - -/** - * whc_std_last - is this sTD the URB's last? - * @std: the sTD to check. - */ -static inline bool whc_std_last(struct whc_std *std) -{ - return std->ntds_remaining <= 1; -} - -enum whc_update { - WHC_UPDATE_ADDED = 0x01, - WHC_UPDATE_REMOVED = 0x02, - WHC_UPDATE_UPDATED = 0x04, -}; - -/* init.c */ -int whc_init(struct whc *whc); -void whc_clean_up(struct whc *whc); - -/* hw.c */ -void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val); -int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len); -void whc_hw_error(struct whc *whc, const char *reason); - -/* wusb.c */ -int whc_wusbhc_start(struct wusbhc *wusbhc); -void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay); -int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, - u8 handle, struct wuie_hdr *wuie); -int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle); -int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm); -int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev); -int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots); -int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, - const void *ptk, size_t key_size); -int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid, - const void *gtk, size_t key_size); -int whc_set_cluster_id(struct whc *whc, u8 bcid); - -/* int.c */ -irqreturn_t whc_int_handler(struct usb_hcd *hcd); -void whc_dn_work(struct work_struct *work); - -/* asl.c */ -void asl_start(struct whc *whc); -void asl_stop(struct whc *whc); -int asl_init(struct whc *whc); -void asl_clean_up(struct whc *whc); -int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags); -int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status); -void asl_qset_delete(struct whc *whc, struct whc_qset *qset); -void scan_async_work(struct work_struct *work); - -/* pzl.c */ -int pzl_init(struct whc *whc); -void pzl_clean_up(struct whc *whc); -void pzl_start(struct whc *whc); -void pzl_stop(struct whc *whc); -int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags); -int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status); -void pzl_qset_delete(struct whc *whc, struct whc_qset *qset); -void scan_periodic_work(struct work_struct *work); - -/* qset.c */ -struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags); -void qset_free(struct whc *whc, struct whc_qset *qset); -struct whc_qset *get_qset(struct whc *whc, struct urb *urb, gfp_t mem_flags); -void qset_delete(struct whc *whc, struct whc_qset *qset); -void qset_clear(struct whc *whc, struct whc_qset *qset); -void qset_reset(struct whc *whc, struct whc_qset *qset); -int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb, - gfp_t mem_flags); -void qset_free_std(struct whc *whc, struct whc_std *std); -void qset_remove_urb(struct whc *whc, struct whc_qset *qset, - struct urb *urb, int status); -void process_halted_qtd(struct whc *whc, struct whc_qset *qset, - struct whc_qtd *qtd); -void process_inactive_qtd(struct whc *whc, struct whc_qset *qset, - struct whc_qtd *qtd); -enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset); -void qset_remove_complete(struct whc *whc, struct whc_qset *qset); -void pzl_update(struct whc *whc, uint32_t wusbcmd); -void asl_update(struct whc *whc, uint32_t wusbcmd); - -/* debug.c */ -void whc_dbg_init(struct whc *whc); -void whc_dbg_clean_up(struct whc *whc); - -#endif /* #ifndef __WHCD_H */ diff --git a/drivers/staging/wusbcore/host/whci/whci-hc.h b/drivers/staging/wusbcore/host/whci/whci-hc.h deleted file mode 100644 index 5a86a57a80cc..000000000000 --- a/drivers/staging/wusbcore/host/whci/whci-hc.h +++ /dev/null @@ -1,401 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) data structures. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#ifndef _WHCI_WHCI_HC_H -#define _WHCI_WHCI_HC_H - -#include - -/** - * WHCI_PAGE_SIZE - page size use by WHCI - * - * WHCI assumes that host system uses pages of 4096 octets. - */ -#define WHCI_PAGE_SIZE 4096 - - -/** - * QTD_MAX_TXFER_SIZE - max number of bytes to transfer with a single - * qtd. - * - * This is 2^20 - 1. - */ -#define QTD_MAX_XFER_SIZE 1048575 - - -/** - * struct whc_qtd - Queue Element Transfer Descriptors (qTD) - * - * This describes the data for a bulk, control or interrupt transfer. - * - * [WHCI] section 3.2.4 - */ -struct whc_qtd { - __le32 status; /*< remaining transfer len and transfer status */ - __le32 options; - __le64 page_list_ptr; /*< physical pointer to data buffer page list*/ - __u8 setup[8]; /*< setup data for control transfers */ -} __attribute__((packed)); - -#define QTD_STS_ACTIVE (1 << 31) /* enable execution of transaction */ -#define QTD_STS_HALTED (1 << 30) /* transfer halted */ -#define QTD_STS_DBE (1 << 29) /* data buffer error */ -#define QTD_STS_BABBLE (1 << 28) /* babble detected */ -#define QTD_STS_RCE (1 << 27) /* retry count exceeded */ -#define QTD_STS_LAST_PKT (1 << 26) /* set Last Packet Flag in WUSB header */ -#define QTD_STS_INACTIVE (1 << 25) /* queue set is marked inactive */ -#define QTD_STS_IALT_VALID (1 << 23) /* iAlt field is valid */ -#define QTD_STS_IALT(i) (QTD_STS_IALT_VALID | ((i) << 20)) /* iAlt field */ -#define QTD_STS_LEN(l) ((l) << 0) /* transfer length */ -#define QTD_STS_TO_LEN(s) ((s) & 0x000fffff) - -#define QTD_OPT_IOC (1 << 1) /* page_list_ptr points to buffer directly */ -#define QTD_OPT_SMALL (1 << 0) /* interrupt on complete */ - -/** - * struct whc_itd - Isochronous Queue Element Transfer Descriptors (iTD) - * - * This describes the data and other parameters for an isochronous - * transfer. - * - * [WHCI] section 3.2.5 - */ -struct whc_itd { - __le16 presentation_time; /*< presentation time for OUT transfers */ - __u8 num_segments; /*< number of data segments in segment list */ - __u8 status; /*< command execution status */ - __le32 options; /*< misc transfer options */ - __le64 page_list_ptr; /*< physical pointer to data buffer page list */ - __le64 seg_list_ptr; /*< physical pointer to segment list */ -} __attribute__((packed)); - -#define ITD_STS_ACTIVE (1 << 7) /* enable execution of transaction */ -#define ITD_STS_DBE (1 << 5) /* data buffer error */ -#define ITD_STS_BABBLE (1 << 4) /* babble detected */ -#define ITD_STS_INACTIVE (1 << 1) /* queue set is marked inactive */ - -#define ITD_OPT_IOC (1 << 1) /* interrupt on complete */ -#define ITD_OPT_SMALL (1 << 0) /* page_list_ptr points to buffer directly */ - -/** - * Page list entry. - * - * A TD's page list must contain sufficient page list entries for the - * total data length in the TD. - * - * [WHCI] section 3.2.4.3 - */ -struct whc_page_list_entry { - __le64 buf_ptr; /*< physical pointer to buffer */ -} __attribute__((packed)); - -/** - * struct whc_seg_list_entry - Segment list entry. - * - * Describes a portion of the data buffer described in the containing - * qTD's page list. - * - * seg_ptr = qtd->page_list_ptr[qtd->seg_list_ptr[seg].idx].buf_ptr - * + qtd->seg_list_ptr[seg].offset; - * - * Segments can't cross page boundries. - * - * [WHCI] section 3.2.5.5 - */ -struct whc_seg_list_entry { - __le16 len; /*< segment length */ - __u8 idx; /*< index into page list */ - __u8 status; /*< segment status */ - __le16 offset; /*< 12 bit offset into page */ -} __attribute__((packed)); - -/** - * struct whc_qhead - endpoint and status information for a qset. - * - * [WHCI] section 3.2.6 - */ -struct whc_qhead { - __le64 link; /*< next qset in list */ - __le32 info1; - __le32 info2; - __le32 info3; - __le16 status; - __le16 err_count; /*< transaction error count */ - __le32 cur_window; - __le32 scratch[3]; /*< h/w scratch area */ - union { - struct whc_qtd qtd; - struct whc_itd itd; - } overlay; -} __attribute__((packed)); - -#define QH_LINK_PTR_MASK (~0x03Full) -#define QH_LINK_PTR(ptr) ((ptr) & QH_LINK_PTR_MASK) -#define QH_LINK_IQS (1 << 4) /* isochronous queue set */ -#define QH_LINK_NTDS(n) (((n) - 1) << 1) /* number of TDs in queue set */ -#define QH_LINK_T (1 << 0) /* last queue set in periodic schedule list */ - -#define QH_INFO1_EP(e) ((e) << 0) /* endpoint number */ -#define QH_INFO1_DIR_IN (1 << 4) /* IN transfer */ -#define QH_INFO1_DIR_OUT (0 << 4) /* OUT transfer */ -#define QH_INFO1_TR_TYPE_CTRL (0x0 << 5) /* control transfer */ -#define QH_INFO1_TR_TYPE_ISOC (0x1 << 5) /* isochronous transfer */ -#define QH_INFO1_TR_TYPE_BULK (0x2 << 5) /* bulk transfer */ -#define QH_INFO1_TR_TYPE_INT (0x3 << 5) /* interrupt */ -#define QH_INFO1_TR_TYPE_LP_INT (0x7 << 5) /* low power interrupt */ -#define QH_INFO1_DEV_INFO_IDX(i) ((i) << 8) /* index into device info buffer */ -#define QH_INFO1_SET_INACTIVE (1 << 15) /* set inactive after transfer */ -#define QH_INFO1_MAX_PKT_LEN(l) ((l) << 16) /* maximum packet length */ - -#define QH_INFO2_BURST(b) ((b) << 0) /* maximum burst length */ -#define QH_INFO2_DBP(p) ((p) << 5) /* data burst policy (see [WUSB] table 5-7) */ -#define QH_INFO2_MAX_COUNT(c) ((c) << 8) /* max isoc/int pkts per zone */ -#define QH_INFO2_RQS (1 << 15) /* reactivate queue set */ -#define QH_INFO2_MAX_RETRY(r) ((r) << 16) /* maximum transaction retries */ -#define QH_INFO2_MAX_SEQ(s) ((s) << 20) /* maximum sequence number */ -#define QH_INFO3_MAX_DELAY(d) ((d) << 0) /* maximum stream delay in 125 us units (isoc only) */ -#define QH_INFO3_INTERVAL(i) ((i) << 16) /* segment interval in 125 us units (isoc only) */ - -#define QH_INFO3_TX_RATE(r) ((r) << 24) /* PHY rate (see [ECMA-368] section 10.3.1.1) */ -#define QH_INFO3_TX_PWR(p) ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */ - -#define QH_STATUS_FLOW_CTRL (1 << 15) -#define QH_STATUS_ICUR(i) ((i) << 5) -#define QH_STATUS_TO_ICUR(s) (((s) >> 5) & 0x7) -#define QH_STATUS_SEQ_MASK 0x1f - -/** - * usb_pipe_to_qh_type - USB core pipe type to QH transfer type - * - * Returns the QH type field for a USB core pipe type. - */ -static inline unsigned usb_pipe_to_qh_type(unsigned pipe) -{ - static const unsigned type[] = { - [PIPE_ISOCHRONOUS] = QH_INFO1_TR_TYPE_ISOC, - [PIPE_INTERRUPT] = QH_INFO1_TR_TYPE_INT, - [PIPE_CONTROL] = QH_INFO1_TR_TYPE_CTRL, - [PIPE_BULK] = QH_INFO1_TR_TYPE_BULK, - }; - return type[usb_pipetype(pipe)]; -} - -/** - * Maxiumum number of TDs in a qset. - */ -#define WHCI_QSET_TD_MAX 8 - -/** - * struct whc_qset - WUSB data transfers to a specific endpoint - * @qh: the QHead of this qset - * @qtd: up to 8 qTDs (for qsets for control, bulk and interrupt - * transfers) - * @itd: up to 8 iTDs (for qsets for isochronous transfers) - * @qset_dma: DMA address for this qset - * @whc: WHCI HC this qset is for - * @ep: endpoint - * @stds: list of sTDs queued to this qset - * @ntds: number of qTDs queued (not necessarily the same as nTDs - * field in the QH) - * @td_start: index of the first qTD in the list - * @td_end: index of next free qTD in the list (provided - * ntds < WHCI_QSET_TD_MAX) - * - * Queue Sets (qsets) are added to the asynchronous schedule list - * (ASL) or the periodic zone list (PZL). - * - * qsets may contain up to 8 TDs (either qTDs or iTDs as appropriate). - * Each TD may refer to at most 1 MiB of data. If a single transfer - * has > 8MiB of data, TDs can be reused as they are completed since - * the TD list is used as a circular buffer. Similarly, several - * (smaller) transfers may be queued in a qset. - * - * WHCI controllers may cache portions of the qsets in the ASL and - * PZL, requiring the WHCD to inform the WHC that the lists have been - * updated (fields changed or qsets inserted or removed). For safe - * insertion and removal of qsets from the lists the schedule must be - * stopped to avoid races in updating the QH link pointers. - * - * Since the HC is free to execute qsets in any order, all transfers - * to an endpoint should use the same qset to ensure transfers are - * executed in the order they're submitted. - * - * [WHCI] section 3.2.3 - */ -struct whc_qset { - struct whc_qhead qh; - union { - struct whc_qtd qtd[WHCI_QSET_TD_MAX]; - struct whc_itd itd[WHCI_QSET_TD_MAX]; - }; - - /* private data for WHCD */ - dma_addr_t qset_dma; - struct whc *whc; - struct usb_host_endpoint *ep; - struct list_head stds; - int ntds; - int td_start; - int td_end; - struct list_head list_node; - unsigned in_sw_list:1; - unsigned in_hw_list:1; - unsigned remove:1; - unsigned reset:1; - struct urb *pause_after_urb; - struct completion remove_complete; - uint16_t max_packet; - uint8_t max_burst; - uint8_t max_seq; -}; - -static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target) -{ - if (target) - *ptr = (*ptr & ~(QH_LINK_PTR_MASK | QH_LINK_T)) | QH_LINK_PTR(target); - else - *ptr = QH_LINK_T; -} - -/** - * struct di_buf_entry - Device Information (DI) buffer entry. - * - * There's one of these per connected device. - */ -struct di_buf_entry { - __le32 availability_info[8]; /*< MAS availability information, one MAS per bit */ - __le32 addr_sec_info; /*< addressing and security info */ - __le32 reserved[7]; -} __attribute__((packed)); - -#define WHC_DI_SECURE (1 << 31) -#define WHC_DI_DISABLE (1 << 30) -#define WHC_DI_KEY_IDX(k) ((k) << 8) -#define WHC_DI_KEY_IDX_MASK 0x0000ff00 -#define WHC_DI_DEV_ADDR(a) ((a) << 0) -#define WHC_DI_DEV_ADDR_MASK 0x000000ff - -/** - * struct dn_buf_entry - Device Notification (DN) buffer entry. - * - * [WHCI] section 3.2.8 - */ -struct dn_buf_entry { - __u8 msg_size; /*< number of octets of valid DN data */ - __u8 reserved1; - __u8 src_addr; /*< source address */ - __u8 status; /*< buffer entry status */ - __le32 tkid; /*< TKID for source device, valid if secure bit is set */ - __u8 dn_data[56]; /*< up to 56 octets of DN data */ -} __attribute__((packed)); - -#define WHC_DN_STATUS_VALID (1 << 7) /* buffer entry is valid */ -#define WHC_DN_STATUS_SECURE (1 << 6) /* notification received using secure frame */ - -#define WHC_N_DN_ENTRIES (4096 / sizeof(struct dn_buf_entry)) - -/* The Add MMC IE WUSB Generic Command may take up to 256 bytes of - data. [WHCI] section 2.4.7. */ -#define WHC_GEN_CMD_DATA_LEN 256 - -/* - * HC registers. - * - * [WHCI] section 2.4 - */ - -#define WHCIVERSION 0x00 - -#define WHCSPARAMS 0x04 -# define WHCSPARAMS_TO_N_MMC_IES(p) (((p) >> 16) & 0xff) -# define WHCSPARAMS_TO_N_KEYS(p) (((p) >> 8) & 0xff) -# define WHCSPARAMS_TO_N_DEVICES(p) (((p) >> 0) & 0x7f) - -#define WUSBCMD 0x08 -# define WUSBCMD_BCID(b) ((b) << 16) -# define WUSBCMD_BCID_MASK (0xff << 16) -# define WUSBCMD_ASYNC_QSET_RM (1 << 12) -# define WUSBCMD_PERIODIC_QSET_RM (1 << 11) -# define WUSBCMD_WUSBSI(s) ((s) << 8) -# define WUSBCMD_WUSBSI_MASK (0x7 << 8) -# define WUSBCMD_ASYNC_SYNCED_DB (1 << 7) -# define WUSBCMD_PERIODIC_SYNCED_DB (1 << 6) -# define WUSBCMD_ASYNC_UPDATED (1 << 5) -# define WUSBCMD_PERIODIC_UPDATED (1 << 4) -# define WUSBCMD_ASYNC_EN (1 << 3) -# define WUSBCMD_PERIODIC_EN (1 << 2) -# define WUSBCMD_WHCRESET (1 << 1) -# define WUSBCMD_RUN (1 << 0) - -#define WUSBSTS 0x0c -# define WUSBSTS_ASYNC_SCHED (1 << 15) -# define WUSBSTS_PERIODIC_SCHED (1 << 14) -# define WUSBSTS_DNTS_SCHED (1 << 13) -# define WUSBSTS_HCHALTED (1 << 12) -# define WUSBSTS_GEN_CMD_DONE (1 << 9) -# define WUSBSTS_CHAN_TIME_ROLLOVER (1 << 8) -# define WUSBSTS_DNTS_OVERFLOW (1 << 7) -# define WUSBSTS_BPST_ADJUSTMENT_CHANGED (1 << 6) -# define WUSBSTS_HOST_ERR (1 << 5) -# define WUSBSTS_ASYNC_SCHED_SYNCED (1 << 4) -# define WUSBSTS_PERIODIC_SCHED_SYNCED (1 << 3) -# define WUSBSTS_DNTS_INT (1 << 2) -# define WUSBSTS_ERR_INT (1 << 1) -# define WUSBSTS_INT (1 << 0) -# define WUSBSTS_INT_MASK 0x3ff - -#define WUSBINTR 0x10 -# define WUSBINTR_GEN_CMD_DONE (1 << 9) -# define WUSBINTR_CHAN_TIME_ROLLOVER (1 << 8) -# define WUSBINTR_DNTS_OVERFLOW (1 << 7) -# define WUSBINTR_BPST_ADJUSTMENT_CHANGED (1 << 6) -# define WUSBINTR_HOST_ERR (1 << 5) -# define WUSBINTR_ASYNC_SCHED_SYNCED (1 << 4) -# define WUSBINTR_PERIODIC_SCHED_SYNCED (1 << 3) -# define WUSBINTR_DNTS_INT (1 << 2) -# define WUSBINTR_ERR_INT (1 << 1) -# define WUSBINTR_INT (1 << 0) -# define WUSBINTR_ALL 0x3ff - -#define WUSBGENCMDSTS 0x14 -# define WUSBGENCMDSTS_ACTIVE (1 << 31) -# define WUSBGENCMDSTS_ERROR (1 << 24) -# define WUSBGENCMDSTS_IOC (1 << 23) -# define WUSBGENCMDSTS_MMCIE_ADD 0x01 -# define WUSBGENCMDSTS_MMCIE_RM 0x02 -# define WUSBGENCMDSTS_SET_MAS 0x03 -# define WUSBGENCMDSTS_CHAN_STOP 0x04 -# define WUSBGENCMDSTS_RWP_EN 0x05 - -#define WUSBGENCMDPARAMS 0x18 -#define WUSBGENADDR 0x20 -#define WUSBASYNCLISTADDR 0x28 -#define WUSBDNTSBUFADDR 0x30 -#define WUSBDEVICEINFOADDR 0x38 - -#define WUSBSETSECKEYCMD 0x40 -# define WUSBSETSECKEYCMD_SET (1 << 31) -# define WUSBSETSECKEYCMD_ERASE (1 << 30) -# define WUSBSETSECKEYCMD_GTK (1 << 8) -# define WUSBSETSECKEYCMD_IDX(i) ((i) << 0) - -#define WUSBTKID 0x44 -#define WUSBSECKEY 0x48 -#define WUSBPERIODICLISTBASE 0x58 -#define WUSBMASINDEX 0x60 - -#define WUSBDNTSCTRL 0x64 -# define WUSBDNTSCTRL_ACTIVE (1 << 31) -# define WUSBDNTSCTRL_INTERVAL(i) ((i) << 8) -# define WUSBDNTSCTRL_SLOTS(s) ((s) << 0) - -#define WUSBTIME 0x68 -# define WUSBTIME_CHANNEL_TIME_MASK 0x00ffffff - -#define WUSBBPST 0x6c -#define WUSBDIBUPDATED 0x70 - -#endif /* #ifndef _WHCI_WHCI_HC_H */ diff --git a/drivers/staging/wusbcore/host/whci/wusb.c b/drivers/staging/wusbcore/host/whci/wusb.c deleted file mode 100644 index 6d0068ab35e4..000000000000 --- a/drivers/staging/wusbcore/host/whci/wusb.c +++ /dev/null @@ -1,210 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) WUSB operations. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include - -#include "../../../uwb/include/umc.h" -#include "../../wusbhc.h" - -#include "whcd.h" - -static int whc_update_di(struct whc *whc, int idx) -{ - int offset = idx / 32; - u32 bit = 1 << (idx % 32); - - le_writel(bit, whc->base + WUSBDIBUPDATED + offset); - - return whci_wait_for(&whc->umc->dev, - whc->base + WUSBDIBUPDATED + offset, bit, 0, - 100, "DI update"); -} - -/* - * WHCI starts MMCs based on there being a valid GTK so these need - * only start/stop the asynchronous and periodic schedules and send a - * channel stop command. - */ - -int whc_wusbhc_start(struct wusbhc *wusbhc) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - - asl_start(whc); - pzl_start(whc); - - return 0; -} - -void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - u32 stop_time, now_time; - int ret; - - pzl_stop(whc); - asl_stop(whc); - - now_time = le_readl(whc->base + WUSBTIME) & WUSBTIME_CHANNEL_TIME_MASK; - stop_time = (now_time + ((delay * 8) << 7)) & 0x00ffffff; - ret = whc_do_gencmd(whc, WUSBGENCMDSTS_CHAN_STOP, stop_time, NULL, 0); - if (ret == 0) - msleep(delay); -} - -int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, - u8 handle, struct wuie_hdr *wuie) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - u32 params; - - params = (interval << 24) - | (repeat_cnt << 16) - | (wuie->bLength << 8) - | handle; - - return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_ADD, params, wuie, wuie->bLength); -} - -int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - u32 params; - - params = handle; - - return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_RM, params, NULL, 0); -} - -int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - - if (stream_index >= 0) - whc_write_wusbcmd(whc, WUSBCMD_WUSBSI_MASK, WUSBCMD_WUSBSI(stream_index)); - - return whc_do_gencmd(whc, WUSBGENCMDSTS_SET_MAS, 0, (void *)mas_bm, sizeof(*mas_bm)); -} - -int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - int idx = wusb_dev->port_idx; - struct di_buf_entry *di = &whc->di_buf[idx]; - int ret; - - mutex_lock(&whc->mutex); - - uwb_mas_bm_copy_le(di->availability_info, &wusb_dev->availability); - di->addr_sec_info &= ~(WHC_DI_DISABLE | WHC_DI_DEV_ADDR_MASK); - di->addr_sec_info |= WHC_DI_DEV_ADDR(wusb_dev->addr); - - ret = whc_update_di(whc, idx); - - mutex_unlock(&whc->mutex); - - return ret; -} - -/* - * Set the number of Device Notification Time Slots (DNTS) and enable - * device notifications. - */ -int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - u32 dntsctrl; - - dntsctrl = WUSBDNTSCTRL_ACTIVE - | WUSBDNTSCTRL_INTERVAL(interval) - | WUSBDNTSCTRL_SLOTS(slots); - - le_writel(dntsctrl, whc->base + WUSBDNTSCTRL); - - return 0; -} - -static int whc_set_key(struct whc *whc, u8 key_index, uint32_t tkid, - const void *key, size_t key_size, bool is_gtk) -{ - uint32_t setkeycmd; - uint32_t seckey[4]; - int i; - int ret; - - memcpy(seckey, key, key_size); - setkeycmd = WUSBSETSECKEYCMD_SET | WUSBSETSECKEYCMD_IDX(key_index); - if (is_gtk) - setkeycmd |= WUSBSETSECKEYCMD_GTK; - - le_writel(tkid, whc->base + WUSBTKID); - for (i = 0; i < 4; i++) - le_writel(seckey[i], whc->base + WUSBSECKEY + 4*i); - le_writel(setkeycmd, whc->base + WUSBSETSECKEYCMD); - - ret = whci_wait_for(&whc->umc->dev, whc->base + WUSBSETSECKEYCMD, - WUSBSETSECKEYCMD_SET, 0, 100, "set key"); - - return ret; -} - -/** - * whc_set_ptk - set the PTK to use for a device. - * - * The index into the key table for this PTK is the same as the - * device's port index. - */ -int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, - const void *ptk, size_t key_size) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - struct di_buf_entry *di = &whc->di_buf[port_idx]; - int ret; - - mutex_lock(&whc->mutex); - - if (ptk) { - ret = whc_set_key(whc, port_idx, tkid, ptk, key_size, false); - if (ret) - goto out; - - di->addr_sec_info &= ~WHC_DI_KEY_IDX_MASK; - di->addr_sec_info |= WHC_DI_SECURE | WHC_DI_KEY_IDX(port_idx); - } else - di->addr_sec_info &= ~WHC_DI_SECURE; - - ret = whc_update_di(whc, port_idx); -out: - mutex_unlock(&whc->mutex); - return ret; -} - -/** - * whc_set_gtk - set the GTK for subsequent broadcast packets - * - * The GTK is stored in the last entry in the key table (the previous - * N_DEVICES entries are for the per-device PTKs). - */ -int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid, - const void *gtk, size_t key_size) -{ - struct whc *whc = wusbhc_to_whc(wusbhc); - int ret; - - mutex_lock(&whc->mutex); - - ret = whc_set_key(whc, whc->n_devices, tkid, gtk, key_size, true); - - mutex_unlock(&whc->mutex); - - return ret; -} - -int whc_set_cluster_id(struct whc *whc, u8 bcid) -{ - whc_write_wusbcmd(whc, WUSBCMD_BCID_MASK, WUSBCMD_BCID(bcid)); - return 0; -} diff --git a/drivers/staging/wusbcore/include/association.h b/drivers/staging/wusbcore/include/association.h deleted file mode 100644 index d7f3cb9b9db5..000000000000 --- a/drivers/staging/wusbcore/include/association.h +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB - Cable Based Association - * - * Copyright (C) 2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - */ -#ifndef __LINUX_USB_ASSOCIATION_H -#define __LINUX_USB_ASSOCIATION_H - - -/* - * Association attributes - * - * Association Models Supplement to WUSB 1.0 T[3-1] - * - * Each field in the structures has it's ID, it's length and then the - * value. This is the actual definition of the field's ID and its - * length. - */ -struct wusb_am_attr { - __u8 id; - __u8 len; -}; - -/* Different fields defined by the spec */ -#define WUSB_AR_AssociationTypeId { .id = cpu_to_le16(0x0000), .len = cpu_to_le16(2) } -#define WUSB_AR_AssociationSubTypeId { .id = cpu_to_le16(0x0001), .len = cpu_to_le16(2) } -#define WUSB_AR_Length { .id = cpu_to_le16(0x0002), .len = cpu_to_le16(4) } -#define WUSB_AR_AssociationStatus { .id = cpu_to_le16(0x0004), .len = cpu_to_le16(4) } -#define WUSB_AR_LangID { .id = cpu_to_le16(0x0008), .len = cpu_to_le16(2) } -#define WUSB_AR_DeviceFriendlyName { .id = cpu_to_le16(0x000b), .len = cpu_to_le16(64) } /* max */ -#define WUSB_AR_HostFriendlyName { .id = cpu_to_le16(0x000c), .len = cpu_to_le16(64) } /* max */ -#define WUSB_AR_CHID { .id = cpu_to_le16(0x1000), .len = cpu_to_le16(16) } -#define WUSB_AR_CDID { .id = cpu_to_le16(0x1001), .len = cpu_to_le16(16) } -#define WUSB_AR_ConnectionContext { .id = cpu_to_le16(0x1002), .len = cpu_to_le16(48) } -#define WUSB_AR_BandGroups { .id = cpu_to_le16(0x1004), .len = cpu_to_le16(2) } - -/* CBAF Control Requests (AMS1.0[T4-1] */ -enum { - CBAF_REQ_GET_ASSOCIATION_INFORMATION = 0x01, - CBAF_REQ_GET_ASSOCIATION_REQUEST, - CBAF_REQ_SET_ASSOCIATION_RESPONSE -}; - -/* - * CBAF USB-interface defitions - * - * No altsettings, one optional interrupt endpoint. - */ -enum { - CBAF_IFACECLASS = 0xef, - CBAF_IFACESUBCLASS = 0x03, - CBAF_IFACEPROTOCOL = 0x01, -}; - -/* Association Information (AMS1.0[T4-3]) */ -struct wusb_cbaf_assoc_info { - __le16 Length; - __u8 NumAssociationRequests; - __le16 Flags; - __u8 AssociationRequestsArray[]; -} __attribute__((packed)); - -/* Association Request (AMS1.0[T4-4]) */ -struct wusb_cbaf_assoc_request { - __u8 AssociationDataIndex; - __u8 Reserved; - __le16 AssociationTypeId; - __le16 AssociationSubTypeId; - __le32 AssociationTypeInfoSize; -} __attribute__((packed)); - -enum { - AR_TYPE_WUSB = 0x0001, - AR_TYPE_WUSB_RETRIEVE_HOST_INFO = 0x0000, - AR_TYPE_WUSB_ASSOCIATE = 0x0001, -}; - -/* Association Attribute header (AMS1.0[3.8]) */ -struct wusb_cbaf_attr_hdr { - __le16 id; - __le16 len; -} __attribute__((packed)); - -/* Host Info (AMS1.0[T4-7]) (yeah, more headers and fields...) */ -struct wusb_cbaf_host_info { - struct wusb_cbaf_attr_hdr AssociationTypeId_hdr; - __le16 AssociationTypeId; - struct wusb_cbaf_attr_hdr AssociationSubTypeId_hdr; - __le16 AssociationSubTypeId; - struct wusb_cbaf_attr_hdr CHID_hdr; - struct wusb_ckhdid CHID; - struct wusb_cbaf_attr_hdr LangID_hdr; - __le16 LangID; - struct wusb_cbaf_attr_hdr HostFriendlyName_hdr; - __u8 HostFriendlyName[]; -} __attribute__((packed)); - -/* Device Info (AMS1.0[T4-8]) - * - * I still don't get this tag'n'header stuff for each goddamn - * field... - */ -struct wusb_cbaf_device_info { - struct wusb_cbaf_attr_hdr Length_hdr; - __le32 Length; - struct wusb_cbaf_attr_hdr CDID_hdr; - struct wusb_ckhdid CDID; - struct wusb_cbaf_attr_hdr BandGroups_hdr; - __le16 BandGroups; - struct wusb_cbaf_attr_hdr LangID_hdr; - __le16 LangID; - struct wusb_cbaf_attr_hdr DeviceFriendlyName_hdr; - __u8 DeviceFriendlyName[]; -} __attribute__((packed)); - -/* Connection Context; CC_DATA - Success case (AMS1.0[T4-9]) */ -struct wusb_cbaf_cc_data { - struct wusb_cbaf_attr_hdr AssociationTypeId_hdr; - __le16 AssociationTypeId; - struct wusb_cbaf_attr_hdr AssociationSubTypeId_hdr; - __le16 AssociationSubTypeId; - struct wusb_cbaf_attr_hdr Length_hdr; - __le32 Length; - struct wusb_cbaf_attr_hdr ConnectionContext_hdr; - struct wusb_ckhdid CHID; - struct wusb_ckhdid CDID; - struct wusb_ckhdid CK; - struct wusb_cbaf_attr_hdr BandGroups_hdr; - __le16 BandGroups; -} __attribute__((packed)); - -/* CC_DATA - Failure case (AMS1.0[T4-10]) */ -struct wusb_cbaf_cc_data_fail { - struct wusb_cbaf_attr_hdr AssociationTypeId_hdr; - __le16 AssociationTypeId; - struct wusb_cbaf_attr_hdr AssociationSubTypeId_hdr; - __le16 AssociationSubTypeId; - struct wusb_cbaf_attr_hdr Length_hdr; - __le16 Length; - struct wusb_cbaf_attr_hdr AssociationStatus_hdr; - __u32 AssociationStatus; -} __attribute__((packed)); - -#endif /* __LINUX_USB_ASSOCIATION_H */ diff --git a/drivers/staging/wusbcore/include/wusb-wa.h b/drivers/staging/wusbcore/include/wusb-wa.h deleted file mode 100644 index 64a840b5106e..000000000000 --- a/drivers/staging/wusbcore/include/wusb-wa.h +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB Wire Adapter constants and structures. - * - * Copyright (C) 2005-2006 Intel Corporation. - * Inaky Perez-Gonzalez - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: docs - * FIXME: organize properly, group logically - * - * All the event structures are defined in uwb/spec.h, as they are - * common to the WHCI and WUSB radio control interfaces. - * - * References: - * [WUSB] Wireless Universal Serial Bus Specification, revision 1.0, ch8 - */ -#ifndef __LINUX_USB_WUSB_WA_H -#define __LINUX_USB_WUSB_WA_H - -/** - * Radio Command Request for the Radio Control Interface - * - * Radio Control Interface command and event codes are the same as - * WHCI, and listed in include/linux/uwb.h:UWB_RC_{CMD,EVT}_* - */ -enum { - WA_EXEC_RC_CMD = 40, /* Radio Control command Request */ -}; - -/* Wireless Adapter Requests ([WUSB] table 8-51) */ -enum { - WUSB_REQ_ADD_MMC_IE = 20, - WUSB_REQ_REMOVE_MMC_IE = 21, - WUSB_REQ_SET_NUM_DNTS = 22, - WUSB_REQ_SET_CLUSTER_ID = 23, - WUSB_REQ_SET_DEV_INFO = 24, - WUSB_REQ_GET_TIME = 25, - WUSB_REQ_SET_STREAM_IDX = 26, - WUSB_REQ_SET_WUSB_MAS = 27, - WUSB_REQ_CHAN_STOP = 28, -}; - - -/* Wireless Adapter WUSB Channel Time types ([WUSB] table 8-52) */ -enum { - WUSB_TIME_ADJ = 0, - WUSB_TIME_BPST = 1, - WUSB_TIME_WUSB = 2, -}; - -enum { - WA_ENABLE = 0x01, - WA_RESET = 0x02, - RPIPE_PAUSE = 0x1, - RPIPE_STALL = 0x2, -}; - -/* Responses from Get Status request ([WUSB] section 8.3.1.6) */ -enum { - WA_STATUS_ENABLED = 0x01, - WA_STATUS_RESETTING = 0x02 -}; - -enum rpipe_crs { - RPIPE_CRS_CTL = 0x01, - RPIPE_CRS_ISO = 0x02, - RPIPE_CRS_BULK = 0x04, - RPIPE_CRS_INTR = 0x08 -}; - -/** - * RPipe descriptor ([WUSB] section 8.5.2.11) - * - * FIXME: explain rpipes - */ -struct usb_rpipe_descriptor { - u8 bLength; - u8 bDescriptorType; - __le16 wRPipeIndex; - __le16 wRequests; - __le16 wBlocks; /* rw if 0 */ - __le16 wMaxPacketSize; /* rw */ - union { - u8 dwa_bHSHubAddress; /* rw: DWA. */ - u8 hwa_bMaxBurst; /* rw: HWA. */ - }; - union { - u8 dwa_bHSHubPort; /* rw: DWA. */ - u8 hwa_bDeviceInfoIndex; /* rw: HWA. */ - }; - u8 bSpeed; /* rw: xfer rate 'enum uwb_phy_rate' */ - union { - u8 dwa_bDeviceAddress; /* rw: DWA Target device address. */ - u8 hwa_reserved; /* rw: HWA. */ - }; - u8 bEndpointAddress; /* rw: Target EP address */ - u8 bDataSequence; /* ro: Current Data sequence */ - __le32 dwCurrentWindow; /* ro */ - u8 bMaxDataSequence; /* ro?: max supported seq */ - u8 bInterval; /* rw: */ - u8 bOverTheAirInterval; /* rw: */ - u8 bmAttribute; /* ro? */ - u8 bmCharacteristics; /* ro? enum rpipe_attr, supported xsactions */ - u8 bmRetryOptions; /* rw? */ - __le16 wNumTransactionErrors; /* rw */ -} __attribute__ ((packed)); - -/** - * Wire Adapter Notification types ([WUSB] sections 8.4.5 & 8.5.4) - * - * These are the notifications coming on the notification endpoint of - * an HWA and a DWA. - */ -enum wa_notif_type { - DWA_NOTIF_RWAKE = 0x91, - DWA_NOTIF_PORTSTATUS = 0x92, - WA_NOTIF_TRANSFER = 0x93, - HWA_NOTIF_BPST_ADJ = 0x94, - HWA_NOTIF_DN = 0x95, -}; - -/** - * Wire Adapter notification header - * - * Notifications coming from a wire adapter use a common header - * defined in [WUSB] sections 8.4.5 & 8.5.4. - */ -struct wa_notif_hdr { - u8 bLength; - u8 bNotifyType; /* enum wa_notif_type */ -} __packed; - -/** - * HWA DN Received notification [(WUSB] section 8.5.4.2) - * - * The DNData is specified in WUSB1.0[7.6]. For each device - * notification we received, we just need to dispatch it. - * - * @dndata: this is really an array of notifications, but all start - * with the same header. - */ -struct hwa_notif_dn { - struct wa_notif_hdr hdr; - u8 bSourceDeviceAddr; /* from errata 2005/07 */ - u8 bmAttributes; - struct wusb_dn_hdr dndata[]; -} __packed; - -/* [WUSB] section 8.3.3 */ -enum wa_xfer_type { - WA_XFER_TYPE_CTL = 0x80, - WA_XFER_TYPE_BI = 0x81, /* bulk/interrupt */ - WA_XFER_TYPE_ISO = 0x82, - WA_XFER_RESULT = 0x83, - WA_XFER_ABORT = 0x84, - WA_XFER_ISO_PACKET_INFO = 0xA0, - WA_XFER_ISO_PACKET_STATUS = 0xA1, -}; - -/* [WUSB] section 8.3.3 */ -struct wa_xfer_hdr { - u8 bLength; /* 0x18 */ - u8 bRequestType; /* 0x80 WA_REQUEST_TYPE_CTL */ - __le16 wRPipe; /* RPipe index */ - __le32 dwTransferID; /* Host-assigned ID */ - __le32 dwTransferLength; /* Length of data to xfer */ - u8 bTransferSegment; -} __packed; - -struct wa_xfer_ctl { - struct wa_xfer_hdr hdr; - u8 bmAttribute; - __le16 wReserved; - struct usb_ctrlrequest baSetupData; -} __packed; - -struct wa_xfer_bi { - struct wa_xfer_hdr hdr; - u8 bReserved; - __le16 wReserved; -} __packed; - -/* [WUSB] section 8.5.5 */ -struct wa_xfer_hwaiso { - struct wa_xfer_hdr hdr; - u8 bReserved; - __le16 wPresentationTime; - __le32 dwNumOfPackets; -} __packed; - -struct wa_xfer_packet_info_hwaiso { - __le16 wLength; - u8 bPacketType; - u8 bReserved; - __le16 PacketLength[0]; -} __packed; - -struct wa_xfer_packet_status_len_hwaiso { - __le16 PacketLength; - __le16 PacketStatus; -} __packed; - -struct wa_xfer_packet_status_hwaiso { - __le16 wLength; - u8 bPacketType; - u8 bReserved; - struct wa_xfer_packet_status_len_hwaiso PacketStatus[0]; -} __packed; - -/* [WUSB] section 8.3.3.5 */ -struct wa_xfer_abort { - u8 bLength; - u8 bRequestType; - __le16 wRPipe; /* RPipe index */ - __le32 dwTransferID; /* Host-assigned ID */ -} __packed; - -/** - * WA Transfer Complete notification ([WUSB] section 8.3.3.3) - * - */ -struct wa_notif_xfer { - struct wa_notif_hdr hdr; - u8 bEndpoint; - u8 Reserved; -} __packed; - -/** Transfer result basic codes [WUSB] table 8-15 */ -enum { - WA_XFER_STATUS_SUCCESS, - WA_XFER_STATUS_HALTED, - WA_XFER_STATUS_DATA_BUFFER_ERROR, - WA_XFER_STATUS_BABBLE, - WA_XFER_RESERVED, - WA_XFER_STATUS_NOT_FOUND, - WA_XFER_STATUS_INSUFFICIENT_RESOURCE, - WA_XFER_STATUS_TRANSACTION_ERROR, - WA_XFER_STATUS_ABORTED, - WA_XFER_STATUS_RPIPE_NOT_READY, - WA_XFER_INVALID_FORMAT, - WA_XFER_UNEXPECTED_SEGMENT_NUMBER, - WA_XFER_STATUS_RPIPE_TYPE_MISMATCH, -}; - -/** [WUSB] section 8.3.3.4 */ -struct wa_xfer_result { - struct wa_notif_hdr hdr; - __le32 dwTransferID; - __le32 dwTransferLength; - u8 bTransferSegment; - u8 bTransferStatus; - __le32 dwNumOfPackets; -} __packed; - -/** - * Wire Adapter Class Descriptor ([WUSB] section 8.5.2.7). - * - * NOTE: u16 fields are read Little Endian from the hardware. - * - * @bNumPorts is the original max number of devices that the host can - * connect; we might chop this so the stack can handle - * it. In case you need to access it, use wusbhc->ports_max - * if it is a Wireless USB WA. - */ -struct usb_wa_descriptor { - u8 bLength; - u8 bDescriptorType; - __le16 bcdWAVersion; - u8 bNumPorts; /* don't use!! */ - u8 bmAttributes; /* Reserved == 0 */ - __le16 wNumRPipes; - __le16 wRPipeMaxBlock; - u8 bRPipeBlockSize; - u8 bPwrOn2PwrGood; - u8 bNumMMCIEs; - u8 DeviceRemovable; /* FIXME: in DWA this is up to 16 bytes */ -} __packed; - -/** - * HWA Device Information Buffer (WUSB1.0[T8.54]) - */ -struct hwa_dev_info { - u8 bmDeviceAvailability[32]; /* FIXME: ignored for now */ - u8 bDeviceAddress; - __le16 wPHYRates; - u8 bmDeviceAttribute; -} __packed; - -#endif /* #ifndef __LINUX_USB_WUSB_WA_H */ diff --git a/drivers/staging/wusbcore/include/wusb.h b/drivers/staging/wusbcore/include/wusb.h deleted file mode 100644 index 09771d1da7bc..000000000000 --- a/drivers/staging/wusbcore/include/wusb.h +++ /dev/null @@ -1,362 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB Standard Definitions - * Event Size Tables - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * - * FIXME: docs - * FIXME: organize properly, group logically - * - * All the event structures are defined in uwb/spec.h, as they are - * common to the WHCI and WUSB radio control interfaces. - */ - -#ifndef __WUSB_H__ -#define __WUSB_H__ - -#include -#include -#include -#include -#include "../../uwb/include/spec.h" - -/** - * WUSB Information Element header - * - * I don't know why, they decided to make it different to the MBOA MAC - * IE Header; beats me. - */ -struct wuie_hdr { - u8 bLength; - u8 bIEIdentifier; -} __attribute__((packed)); - -enum { - WUIE_ID_WCTA = 0x80, - WUIE_ID_CONNECTACK, - WUIE_ID_HOST_INFO, - WUIE_ID_CHANGE_ANNOUNCE, - WUIE_ID_DEVICE_DISCONNECT, - WUIE_ID_HOST_DISCONNECT, - WUIE_ID_KEEP_ALIVE = 0x89, - WUIE_ID_ISOCH_DISCARD, - WUIE_ID_RESET_DEVICE, -}; - -/** - * Maximum number of array elements in a WUSB IE. - * - * WUSB1.0[7.5 before table 7-38] says that in WUSB IEs that - * are "arrays" have to limited to 4 elements. So we define it - * like that to ease up and submit only the neeed size. - */ -#define WUIE_ELT_MAX 4 - -/** - * Wrapper for the data that defines a CHID, a CDID or a CK - * - * WUSB defines that CHIDs, CDIDs and CKs are a 16 byte string of - * data. In order to avoid confusion and enforce types, we wrap it. - * - * Make it packed, as we use it in some hw definitions. - */ -struct wusb_ckhdid { - u8 data[16]; -} __attribute__((packed)); - -static const struct wusb_ckhdid wusb_ckhdid_zero = { .data = { 0 } }; - -#define WUSB_CKHDID_STRSIZE (3 * sizeof(struct wusb_ckhdid) + 1) - -/** - * WUSB IE: Host Information (WUSB1.0[7.5.2]) - * - * Used to provide information about the host to the Wireless USB - * devices in range (CHID can be used as an ASCII string). - */ -struct wuie_host_info { - struct wuie_hdr hdr; - __le16 attributes; - struct wusb_ckhdid CHID; -} __attribute__((packed)); - -/** - * WUSB IE: Connect Ack (WUSB1.0[7.5.1]) - * - * Used to acknowledge device connect requests. See note for - * WUIE_ELT_MAX. - */ -struct wuie_connect_ack { - struct wuie_hdr hdr; - struct { - struct wusb_ckhdid CDID; - u8 bDeviceAddress; /* 0 means unused */ - u8 bReserved; - } blk[WUIE_ELT_MAX]; -} __attribute__((packed)); - -/** - * WUSB IE Host Information Element, Connect Availability - * - * WUSB1.0[7.5.2], bmAttributes description - */ -enum { - WUIE_HI_CAP_RECONNECT = 0, - WUIE_HI_CAP_LIMITED, - WUIE_HI_CAP_RESERVED, - WUIE_HI_CAP_ALL, -}; - -/** - * WUSB IE: Channel Stop (WUSB1.0[7.5.8]) - * - * Tells devices the host is going to stop sending MMCs and will disappear. - */ -struct wuie_channel_stop { - struct wuie_hdr hdr; - u8 attributes; - u8 timestamp[3]; -} __attribute__((packed)); - -/** - * WUSB IE: Keepalive (WUSB1.0[7.5.9]) - * - * Ask device(s) to send keepalives. - */ -struct wuie_keep_alive { - struct wuie_hdr hdr; - u8 bDeviceAddress[WUIE_ELT_MAX]; -} __attribute__((packed)); - -/** - * WUSB IE: Reset device (WUSB1.0[7.5.11]) - * - * Tell device to reset; in all truth, we can fit 4 CDIDs, but we only - * use it for one at the time... - * - * In any case, this request is a wee bit silly: why don't they target - * by address?? - */ -struct wuie_reset { - struct wuie_hdr hdr; - struct wusb_ckhdid CDID; -} __attribute__((packed)); - -/** - * WUSB IE: Disconnect device (WUSB1.0[7.5.11]) - * - * Tell device to disconnect; we can fit 4 addresses, but we only use - * it for one at the time... - */ -struct wuie_disconnect { - struct wuie_hdr hdr; - u8 bDeviceAddress; - u8 padding; -} __attribute__((packed)); - -/** - * WUSB IE: Host disconnect ([WUSB] section 7.5.5) - * - * Tells all connected devices to disconnect. - */ -struct wuie_host_disconnect { - struct wuie_hdr hdr; -} __attribute__((packed)); - -/** - * WUSB Device Notification header (WUSB1.0[7.6]) - */ -struct wusb_dn_hdr { - u8 bType; - u8 notifdata[]; -} __attribute__((packed)); - -/** Device Notification codes (WUSB1.0[Table 7-54]) */ -enum WUSB_DN { - WUSB_DN_CONNECT = 0x01, - WUSB_DN_DISCONNECT = 0x02, - WUSB_DN_EPRDY = 0x03, - WUSB_DN_MASAVAILCHANGED = 0x04, - WUSB_DN_RWAKE = 0x05, - WUSB_DN_SLEEP = 0x06, - WUSB_DN_ALIVE = 0x07, -}; - -/** WUSB Device Notification Connect */ -struct wusb_dn_connect { - struct wusb_dn_hdr hdr; - __le16 attributes; - struct wusb_ckhdid CDID; -} __attribute__((packed)); - -static inline int wusb_dn_connect_prev_dev_addr(const struct wusb_dn_connect *dn) -{ - return le16_to_cpu(dn->attributes) & 0xff; -} - -static inline int wusb_dn_connect_new_connection(const struct wusb_dn_connect *dn) -{ - return (le16_to_cpu(dn->attributes) >> 8) & 0x1; -} - -static inline int wusb_dn_connect_beacon_behavior(const struct wusb_dn_connect *dn) -{ - return (le16_to_cpu(dn->attributes) >> 9) & 0x03; -} - -/** Device is alive (aka: pong) (WUSB1.0[7.6.7]) */ -struct wusb_dn_alive { - struct wusb_dn_hdr hdr; -} __attribute__((packed)); - -/** Device is disconnecting (WUSB1.0[7.6.2]) */ -struct wusb_dn_disconnect { - struct wusb_dn_hdr hdr; -} __attribute__((packed)); - -/* General constants */ -enum { - WUSB_TRUST_TIMEOUT_MS = 4000, /* [WUSB] section 4.15.1 */ -}; - -/* - * WUSB Crypto stuff (WUSB1.0[6]) - */ - -extern const char *wusb_et_name(u8); - -/** - * WUSB key index WUSB1.0[7.3.2.4], for usage when setting keys for - * the host or the device. - */ -static inline u8 wusb_key_index(int index, int type, int originator) -{ - return (originator << 6) | (type << 4) | index; -} - -#define WUSB_KEY_INDEX_TYPE_PTK 0 /* for HWA only */ -#define WUSB_KEY_INDEX_TYPE_ASSOC 1 -#define WUSB_KEY_INDEX_TYPE_GTK 2 -#define WUSB_KEY_INDEX_ORIGINATOR_HOST 0 -#define WUSB_KEY_INDEX_ORIGINATOR_DEVICE 1 -/* bits 0-3 used for the key index. */ -#define WUSB_KEY_INDEX_MAX 15 - -/* A CCM Nonce, defined in WUSB1.0[6.4.1] */ -struct aes_ccm_nonce { - u8 sfn[6]; /* Little Endian */ - u8 tkid[3]; /* LE */ - struct uwb_dev_addr dest_addr; - struct uwb_dev_addr src_addr; -} __attribute__((packed)); - -/* A CCM operation label, defined on WUSB1.0[6.5.x] */ -struct aes_ccm_label { - u8 data[14]; -} __attribute__((packed)); - -/* - * Input to the key derivation sequence defined in - * WUSB1.0[6.5.1]. Rest of the data is in the CCM Nonce passed to the - * PRF function. - */ -struct wusb_keydvt_in { - u8 hnonce[16]; - u8 dnonce[16]; -} __attribute__((packed)); - -/* - * Output from the key derivation sequence defined in - * WUSB1.0[6.5.1]. - */ -struct wusb_keydvt_out { - u8 kck[16]; - u8 ptk[16]; -} __attribute__((packed)); - -/* Pseudo Random Function WUSB1.0[6.5] */ -extern int wusb_crypto_init(void); -extern void wusb_crypto_exit(void); -extern ssize_t wusb_prf(void *out, size_t out_size, - const u8 key[16], const struct aes_ccm_nonce *_n, - const struct aes_ccm_label *a, - const void *b, size_t blen, size_t len); - -static inline int wusb_prf_64(void *out, size_t out_size, const u8 key[16], - const struct aes_ccm_nonce *n, - const struct aes_ccm_label *a, - const void *b, size_t blen) -{ - return wusb_prf(out, out_size, key, n, a, b, blen, 64); -} - -static inline int wusb_prf_128(void *out, size_t out_size, const u8 key[16], - const struct aes_ccm_nonce *n, - const struct aes_ccm_label *a, - const void *b, size_t blen) -{ - return wusb_prf(out, out_size, key, n, a, b, blen, 128); -} - -static inline int wusb_prf_256(void *out, size_t out_size, const u8 key[16], - const struct aes_ccm_nonce *n, - const struct aes_ccm_label *a, - const void *b, size_t blen) -{ - return wusb_prf(out, out_size, key, n, a, b, blen, 256); -} - -/* Key derivation WUSB1.0[6.5.1] */ -static inline int wusb_key_derive(struct wusb_keydvt_out *keydvt_out, - const u8 key[16], - const struct aes_ccm_nonce *n, - const struct wusb_keydvt_in *keydvt_in) -{ - const struct aes_ccm_label a = { .data = "Pair-wise keys" }; - return wusb_prf_256(keydvt_out, sizeof(*keydvt_out), key, n, &a, - keydvt_in, sizeof(*keydvt_in)); -} - -/* - * Out-of-band MIC Generation WUSB1.0[6.5.2] - * - * Compute the MIC over @key, @n and @hs and place it in @mic_out. - * - * @mic_out: Where to place the 8 byte MIC tag - * @key: KCK from the derivation process - * @n: CCM nonce, n->sfn == 0, TKID as established in the - * process. - * @hs: Handshake struct for phase 2 of the 4-way. - * hs->bStatus and hs->bReserved are zero. - * hs->bMessageNumber is 2 (WUSB1.0[7.3.2.5.2] - * hs->dest_addr is the device's USB address padded with 0 - * hs->src_addr is the hosts's UWB device address - * hs->mic is ignored (as we compute that value). - */ -static inline int wusb_oob_mic(u8 mic_out[8], const u8 key[16], - const struct aes_ccm_nonce *n, - const struct usb_handshake *hs) -{ - const struct aes_ccm_label a = { .data = "out-of-bandMIC" }; - return wusb_prf_64(mic_out, 8, key, n, &a, - hs, sizeof(*hs) - sizeof(hs->MIC)); -} - -#endif /* #ifndef __WUSB_H__ */ diff --git a/drivers/staging/wusbcore/mmc.c b/drivers/staging/wusbcore/mmc.c deleted file mode 100644 index 881e1f20d718..000000000000 --- a/drivers/staging/wusbcore/mmc.c +++ /dev/null @@ -1,303 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) - * MMC (Microscheduled Management Command) handling - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * WUIEs and MMC IEs...well, they are almost the same at the end. MMC - * IEs are Wireless USB IEs that go into the MMC period...[what is - * that? look in Design-overview.txt]. - * - * - * This is a simple subsystem to keep track of which IEs are being - * sent by the host in the MMC period. - * - * For each WUIE we ask to send, we keep it in an array, so we can - * request its removal later, or replace the content. They are tracked - * by pointer, so be sure to use the same pointer if you want to - * remove it or update the contents. - * - * FIXME: - * - add timers that autoremove intervalled IEs? - */ -#include -#include -#include "include/wusb.h" -#include "wusbhc.h" - -/* Initialize the MMCIEs handling mechanism */ -int wusbhc_mmcie_create(struct wusbhc *wusbhc) -{ - u8 mmcies = wusbhc->mmcies_max; - wusbhc->mmcie = kcalloc(mmcies, sizeof(wusbhc->mmcie[0]), GFP_KERNEL); - if (wusbhc->mmcie == NULL) - return -ENOMEM; - mutex_init(&wusbhc->mmcie_mutex); - return 0; -} - -/* Release resources used by the MMCIEs handling mechanism */ -void wusbhc_mmcie_destroy(struct wusbhc *wusbhc) -{ - kfree(wusbhc->mmcie); -} - -/* - * Add or replace an MMC Wireless USB IE. - * - * @interval: See WUSB1.0[8.5.3.1] - * @repeat_cnt: See WUSB1.0[8.5.3.1] - * @handle: See WUSB1.0[8.5.3.1] - * @wuie: Pointer to the header of the WUSB IE data to add. - * MUST BE allocated in a kmalloc buffer (no stack or - * vmalloc). - * THE CALLER ALWAYS OWNS THE POINTER (we don't free it - * on remove, we just forget about it). - * @returns: 0 if ok, < 0 errno code on error. - * - * Goes over the *whole* @wusbhc->mmcie array looking for (a) the - * first free spot and (b) if @wuie is already in the array (aka: - * transmitted in the MMCs) the spot were it is. - * - * If present, we "overwrite it" (update). - * - * - * NOTE: Need special ordering rules -- see below WUSB1.0 Table 7-38. - * The host uses the handle as the 'sort' index. We - * allocate the last one always for the WUIE_ID_HOST_INFO, and - * the rest, first come first serve in inverse order. - * - * Host software must make sure that it adds the other IEs in - * the right order... the host hardware is responsible for - * placing the WCTA IEs in the right place with the other IEs - * set by host software. - * - * NOTE: we can access wusbhc->wa_descr without locking because it is - * read only. - */ -int wusbhc_mmcie_set(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, - struct wuie_hdr *wuie) -{ - int result = -ENOBUFS; - unsigned handle, itr; - - /* Search a handle, taking into account the ordering */ - mutex_lock(&wusbhc->mmcie_mutex); - switch (wuie->bIEIdentifier) { - case WUIE_ID_HOST_INFO: - /* Always last */ - handle = wusbhc->mmcies_max - 1; - break; - case WUIE_ID_ISOCH_DISCARD: - dev_err(wusbhc->dev, "Special ordering case for WUIE ID 0x%x " - "unimplemented\n", wuie->bIEIdentifier); - result = -ENOSYS; - goto error_unlock; - default: - /* search for it or find the last empty slot */ - handle = ~0; - for (itr = 0; itr < wusbhc->mmcies_max - 1; itr++) { - if (wusbhc->mmcie[itr] == wuie) { - handle = itr; - break; - } - if (wusbhc->mmcie[itr] == NULL) - handle = itr; - } - if (handle == ~0) - goto error_unlock; - } - result = (wusbhc->mmcie_add)(wusbhc, interval, repeat_cnt, handle, - wuie); - if (result >= 0) - wusbhc->mmcie[handle] = wuie; -error_unlock: - mutex_unlock(&wusbhc->mmcie_mutex); - return result; -} -EXPORT_SYMBOL_GPL(wusbhc_mmcie_set); - -/* - * Remove an MMC IE previously added with wusbhc_mmcie_set() - * - * @wuie Pointer used to add the WUIE - */ -void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie) -{ - int result; - unsigned handle, itr; - - mutex_lock(&wusbhc->mmcie_mutex); - for (itr = 0; itr < wusbhc->mmcies_max; itr++) { - if (wusbhc->mmcie[itr] == wuie) { - handle = itr; - goto found; - } - } - mutex_unlock(&wusbhc->mmcie_mutex); - return; - -found: - result = (wusbhc->mmcie_rm)(wusbhc, handle); - if (result == 0) - wusbhc->mmcie[itr] = NULL; - mutex_unlock(&wusbhc->mmcie_mutex); -} -EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm); - -static int wusbhc_mmc_start(struct wusbhc *wusbhc) -{ - int ret; - - mutex_lock(&wusbhc->mutex); - ret = wusbhc->start(wusbhc); - if (ret >= 0) - wusbhc->active = 1; - mutex_unlock(&wusbhc->mutex); - - return ret; -} - -static void wusbhc_mmc_stop(struct wusbhc *wusbhc) -{ - mutex_lock(&wusbhc->mutex); - wusbhc->active = 0; - wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS); - mutex_unlock(&wusbhc->mutex); -} - -/* - * wusbhc_start - start transmitting MMCs and accepting connections - * @wusbhc: the HC to start - * - * Establishes a cluster reservation, enables device connections, and - * starts MMCs with appropriate DNTS parameters. - */ -int wusbhc_start(struct wusbhc *wusbhc) -{ - int result; - struct device *dev = wusbhc->dev; - - WARN_ON(wusbhc->wuie_host_info != NULL); - BUG_ON(wusbhc->uwb_rc == NULL); - - result = wusbhc_rsv_establish(wusbhc); - if (result < 0) { - dev_err(dev, "cannot establish cluster reservation: %d\n", - result); - goto error_rsv_establish; - } - - result = wusbhc_devconnect_start(wusbhc); - if (result < 0) { - dev_err(dev, "error enabling device connections: %d\n", - result); - goto error_devconnect_start; - } - - result = wusbhc_sec_start(wusbhc); - if (result < 0) { - dev_err(dev, "error starting security in the HC: %d\n", - result); - goto error_sec_start; - } - - result = wusbhc->set_num_dnts(wusbhc, wusbhc->dnts_interval, - wusbhc->dnts_num_slots); - if (result < 0) { - dev_err(dev, "Cannot set DNTS parameters: %d\n", result); - goto error_set_num_dnts; - } - result = wusbhc_mmc_start(wusbhc); - if (result < 0) { - dev_err(dev, "error starting wusbch: %d\n", result); - goto error_wusbhc_start; - } - - return 0; - -error_wusbhc_start: - wusbhc_sec_stop(wusbhc); -error_set_num_dnts: -error_sec_start: - wusbhc_devconnect_stop(wusbhc); -error_devconnect_start: - wusbhc_rsv_terminate(wusbhc); -error_rsv_establish: - return result; -} - -/* - * wusbhc_stop - stop transmitting MMCs - * @wusbhc: the HC to stop - * - * Stops the WUSB channel and removes the cluster reservation. - */ -void wusbhc_stop(struct wusbhc *wusbhc) -{ - wusbhc_mmc_stop(wusbhc); - wusbhc_sec_stop(wusbhc); - wusbhc_devconnect_stop(wusbhc); - wusbhc_rsv_terminate(wusbhc); -} - -/* - * Set/reset/update a new CHID - * - * Depending on the previous state of the MMCs, start, stop or change - * the sent MMC. This effectively switches the host controller on and - * off (radio wise). - */ -int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) -{ - int result = 0; - - if (memcmp(chid, &wusb_ckhdid_zero, sizeof(*chid)) == 0) - chid = NULL; - - mutex_lock(&wusbhc->mutex); - if (chid) { - if (wusbhc->active) { - mutex_unlock(&wusbhc->mutex); - return -EBUSY; - } - wusbhc->chid = *chid; - } - - /* register with UWB if we haven't already since we are about to start - the radio. */ - if ((chid) && (wusbhc->uwb_rc == NULL)) { - wusbhc->uwb_rc = uwb_rc_get_by_grandpa(wusbhc->dev->parent); - if (wusbhc->uwb_rc == NULL) { - result = -ENODEV; - dev_err(wusbhc->dev, - "Cannot get associated UWB Host Controller\n"); - goto error_rc_get; - } - - result = wusbhc_pal_register(wusbhc); - if (result < 0) { - dev_err(wusbhc->dev, "Cannot register as a UWB PAL\n"); - goto error_pal_register; - } - } - mutex_unlock(&wusbhc->mutex); - - if (chid) - result = uwb_radio_start(&wusbhc->pal); - else if (wusbhc->uwb_rc) - uwb_radio_stop(&wusbhc->pal); - - return result; - -error_pal_register: - uwb_rc_put(wusbhc->uwb_rc); - wusbhc->uwb_rc = NULL; -error_rc_get: - mutex_unlock(&wusbhc->mutex); - - return result; -} -EXPORT_SYMBOL_GPL(wusbhc_chid_set); diff --git a/drivers/staging/wusbcore/pal.c b/drivers/staging/wusbcore/pal.c deleted file mode 100644 index 30f569131471..000000000000 --- a/drivers/staging/wusbcore/pal.c +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB Host Controller - * UWB Protocol Adaptation Layer (PAL) glue. - * - * Copyright (C) 2008 Cambridge Silicon Radio Ltd. - */ -#include "wusbhc.h" - -static void wusbhc_channel_changed(struct uwb_pal *pal, int channel) -{ - struct wusbhc *wusbhc = container_of(pal, struct wusbhc, pal); - - dev_dbg(wusbhc->dev, "%s: channel = %d\n", __func__, channel); - if (channel < 0) - wusbhc_stop(wusbhc); - else - wusbhc_start(wusbhc); -} - -/** - * wusbhc_pal_register - register the WUSB HC as a UWB PAL - * @wusbhc: the WUSB HC - */ -int wusbhc_pal_register(struct wusbhc *wusbhc) -{ - uwb_pal_init(&wusbhc->pal); - - wusbhc->pal.name = "wusbhc"; - wusbhc->pal.device = wusbhc->usb_hcd.self.controller; - wusbhc->pal.rc = wusbhc->uwb_rc; - wusbhc->pal.channel_changed = wusbhc_channel_changed; - - return uwb_pal_register(&wusbhc->pal); -} - -/** - * wusbhc_pal_unregister - unregister the WUSB HC as a UWB PAL - * @wusbhc: the WUSB HC - */ -void wusbhc_pal_unregister(struct wusbhc *wusbhc) -{ - if (wusbhc->uwb_rc) - uwb_pal_unregister(&wusbhc->pal); -} diff --git a/drivers/staging/wusbcore/reservation.c b/drivers/staging/wusbcore/reservation.c deleted file mode 100644 index b921faac698b..000000000000 --- a/drivers/staging/wusbcore/reservation.c +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * WUSB cluster reservation management - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include - -#include "../uwb/uwb.h" -#include "wusbhc.h" - -/* - * WUSB cluster reservations are multicast reservations with the - * broadcast cluster ID (BCID) as the target DevAddr. - * - * FIXME: consider adjusting the reservation depending on what devices - * are attached. - */ - -static int wusbhc_bwa_set(struct wusbhc *wusbhc, u8 stream, - const struct uwb_mas_bm *mas) -{ - if (mas == NULL) - mas = &uwb_mas_bm_zero; - return wusbhc->bwa_set(wusbhc, stream, mas); -} - -/** - * wusbhc_rsv_complete_cb - WUSB HC reservation complete callback - * @rsv: the reservation - * - * Either set or clear the HC's view of the reservation. - * - * FIXME: when a reservation is denied the HC should be stopped. - */ -static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv) -{ - struct wusbhc *wusbhc = rsv->pal_priv; - struct device *dev = wusbhc->dev; - struct uwb_mas_bm mas; - - dev_dbg(dev, "%s: state = %d\n", __func__, rsv->state); - switch (rsv->state) { - case UWB_RSV_STATE_O_ESTABLISHED: - uwb_rsv_get_usable_mas(rsv, &mas); - dev_dbg(dev, "established reservation: %*pb\n", - UWB_NUM_MAS, mas.bm); - wusbhc_bwa_set(wusbhc, rsv->stream, &mas); - break; - case UWB_RSV_STATE_NONE: - dev_dbg(dev, "removed reservation\n"); - wusbhc_bwa_set(wusbhc, 0, NULL); - break; - default: - dev_dbg(dev, "unexpected reservation state: %d\n", rsv->state); - break; - } -} - - -/** - * wusbhc_rsv_establish - establish a reservation for the cluster - * @wusbhc: the WUSB HC requesting a bandwidth reservation - */ -int wusbhc_rsv_establish(struct wusbhc *wusbhc) -{ - struct uwb_rc *rc = wusbhc->uwb_rc; - struct uwb_rsv *rsv; - struct uwb_dev_addr bcid; - int ret; - - if (rc == NULL) - return -ENODEV; - - rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc); - if (rsv == NULL) - return -ENOMEM; - - bcid.data[0] = wusbhc->cluster_id; - bcid.data[1] = 0; - - rsv->target.type = UWB_RSV_TARGET_DEVADDR; - rsv->target.devaddr = bcid; - rsv->type = UWB_DRP_TYPE_PRIVATE; - rsv->max_mas = 256; /* try to get as much as possible */ - rsv->min_mas = 15; /* one MAS per zone */ - rsv->max_interval = 1; /* max latency is one zone */ - rsv->is_multicast = true; - - ret = uwb_rsv_establish(rsv); - if (ret == 0) - wusbhc->rsv = rsv; - else - uwb_rsv_destroy(rsv); - return ret; -} - - -/** - * wusbhc_rsv_terminate - terminate the cluster reservation - * @wusbhc: the WUSB host whose reservation is to be terminated - */ -void wusbhc_rsv_terminate(struct wusbhc *wusbhc) -{ - if (wusbhc->rsv) { - uwb_rsv_terminate(wusbhc->rsv); - uwb_rsv_destroy(wusbhc->rsv); - wusbhc->rsv = NULL; - } -} diff --git a/drivers/staging/wusbcore/rh.c b/drivers/staging/wusbcore/rh.c deleted file mode 100644 index 20c08cd9dcbf..000000000000 --- a/drivers/staging/wusbcore/rh.c +++ /dev/null @@ -1,426 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB Host Controller - * Root Hub operations - * - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * We fake a root hub that has fake ports (as many as simultaneous - * devices the Wireless USB Host Controller can deal with). For each - * port we keep an state in @wusbhc->port[index] identical to the one - * specified in the USB2.0[ch11] spec and some extra device - * information that complements the one in 'struct usb_device' (as - * this lacs a hcpriv pointer). - * - * Note this is common to WHCI and HWA host controllers. - * - * Through here we enable most of the state changes that the USB stack - * will use to connect or disconnect devices. We need to do some - * forced adaptation of Wireless USB device states vs. wired: - * - * USB: WUSB: - * - * Port Powered-off port slot n/a - * Powered-on port slot available - * Disconnected port slot available - * Connected port slot assigned device - * device sent DN_Connect - * device was authenticated - * Enabled device is authenticated, transitioned - * from unauth -> auth -> default address - * -> enabled - * Reset disconnect - * Disable disconnect - * - * This maps the standard USB port states with the WUSB device states - * so we can fake ports without having to modify the USB stack. - * - * FIXME: this process will change in the future - * - * - * ENTRY POINTS - * - * Our entry points into here are, as in hcd.c, the USB stack root hub - * ops defined in the usb_hcd struct: - * - * wusbhc_rh_status_data() Provide hub and port status data bitmap - * - * wusbhc_rh_control() Execution of all the major requests - * you can do to a hub (Set|Clear - * features, get descriptors, status, etc). - * - * wusbhc_rh_[suspend|resume]() That - * - * wusbhc_rh_start_port_reset() ??? unimplemented - */ -#include -#include -#include "wusbhc.h" - -/* - * Reset a fake port - * - * Using a Reset Device IE is too heavyweight as it causes the device - * to enter the UnConnected state and leave the cluster, this can mean - * that when the device reconnects it is connected to a different fake - * port. - * - * Instead, reset authenticated devices with a SetAddress(0), followed - * by a SetAddresss(AuthAddr). - * - * For unauthenticated devices just pretend to reset but do nothing. - * If the device initialization continues to fail it will eventually - * time out after TrustTimeout and enter the UnConnected state. - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. - * - * Supposedly we are the only thread accesing @wusbhc->port; in any - * case, maybe we should move the mutex locking from - * wusbhc_devconnect_auth() to here. - * - * @port_idx refers to the wusbhc's port index, not the USB port number - */ -static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx) -{ - int result = 0; - struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); - struct wusb_dev *wusb_dev = port->wusb_dev; - - if (wusb_dev == NULL) - return -ENOTCONN; - - port->status |= USB_PORT_STAT_RESET; - port->change |= USB_PORT_STAT_C_RESET; - - if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH) - result = 0; - else - result = wusb_dev_update_address(wusbhc, wusb_dev); - - port->status &= ~USB_PORT_STAT_RESET; - port->status |= USB_PORT_STAT_ENABLE; - port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE; - - return result; -} - -/* - * Return the hub change status bitmap - * - * The bits in the change status bitmap are cleared when a - * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4]. - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. - * - * WARNING!! This gets called from atomic context; we cannot get the - * mutex--the only race condition we can find is some bit - * changing just after we copy it, which shouldn't be too - * big of a problem [and we can't make it an spinlock - * because other parts need to take it and sleep] . - * - * @usb_hcd is refcounted, so it won't disappear under us - * and before killing a host, the polling of the root hub - * would be stopped anyway. - */ -int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - size_t cnt, size, bits_set = 0; - - /* WE DON'T LOCK, see comment */ - /* round up to bytes. Hub bit is bit 0 so add 1. */ - size = DIV_ROUND_UP(wusbhc->ports_max + 1, 8); - - /* clear the output buffer. */ - memset(_buf, 0, size); - /* set the bit for each changed port. */ - for (cnt = 0; cnt < wusbhc->ports_max; cnt++) { - - if (wusb_port_by_idx(wusbhc, cnt)->change) { - const int bitpos = cnt+1; - - _buf[bitpos/8] |= (1 << (bitpos % 8)); - bits_set++; - } - } - - return bits_set ? size : 0; -} -EXPORT_SYMBOL_GPL(wusbhc_rh_status_data); - -/* - * Return the hub's descriptor - * - * NOTE: almost cut and paste from ehci-hub.c - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked - */ -static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue, - u16 wIndex, - struct usb_hub_descriptor *descr, - u16 wLength) -{ - u16 temp = 1 + (wusbhc->ports_max / 8); - u8 length = 7 + 2 * temp; - - if (wLength < length) - return -ENOSPC; - descr->bDescLength = 7 + 2 * temp; - descr->bDescriptorType = USB_DT_HUB; /* HUB type */ - descr->bNbrPorts = wusbhc->ports_max; - descr->wHubCharacteristics = cpu_to_le16( - HUB_CHAR_COMMON_LPSM /* All ports power at once */ - | 0x00 /* not part of compound device */ - | HUB_CHAR_NO_OCPM /* No overcurrent protection */ - | 0x00 /* 8 FS think time FIXME ?? */ - | 0x00); /* No port indicators */ - descr->bPwrOn2PwrGood = 0; - descr->bHubContrCurrent = 0; - /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ - memset(&descr->u.hs.DeviceRemovable[0], 0, temp); - memset(&descr->u.hs.DeviceRemovable[temp], 0xff, temp); - return 0; -} - -/* - * Clear a hub feature - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. - * - * Nothing to do, so no locking needed ;) - */ -static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature) -{ - int result; - - switch (feature) { - case C_HUB_LOCAL_POWER: - /* FIXME: maybe plug bit 0 to the power input status, - * if any? - * see wusbhc_rh_get_hub_status() */ - case C_HUB_OVER_CURRENT: - result = 0; - break; - default: - result = -EPIPE; - } - return result; -} - -/* - * Return hub status (it is always zero...) - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. - * - * Nothing to do, so no locking needed ;) - */ -static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf, - u16 wLength) -{ - /* FIXME: maybe plug bit 0 to the power input status (if any)? */ - *buf = 0; - return 0; -} - -/* - * Set a port feature - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. - */ -static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature, - u8 selector, u8 port_idx) -{ - struct device *dev = wusbhc->dev; - - if (port_idx > wusbhc->ports_max) - return -EINVAL; - - switch (feature) { - /* According to USB2.0[11.24.2.13]p2, these features - * are not required to be implemented. */ - case USB_PORT_FEAT_C_OVER_CURRENT: - case USB_PORT_FEAT_C_ENABLE: - case USB_PORT_FEAT_C_SUSPEND: - case USB_PORT_FEAT_C_CONNECTION: - case USB_PORT_FEAT_C_RESET: - return 0; - case USB_PORT_FEAT_POWER: - /* No such thing, but we fake it works */ - mutex_lock(&wusbhc->mutex); - wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER; - mutex_unlock(&wusbhc->mutex); - return 0; - case USB_PORT_FEAT_RESET: - return wusbhc_rh_port_reset(wusbhc, port_idx); - case USB_PORT_FEAT_ENABLE: - case USB_PORT_FEAT_SUSPEND: - dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n", - port_idx, feature, selector); - return -ENOSYS; - default: - dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n", - port_idx, feature, selector); - return -EPIPE; - } - - return 0; -} - -/* - * Clear a port feature... - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. - */ -static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature, - u8 selector, u8 port_idx) -{ - int result = 0; - struct device *dev = wusbhc->dev; - - if (port_idx > wusbhc->ports_max) - return -EINVAL; - - mutex_lock(&wusbhc->mutex); - switch (feature) { - case USB_PORT_FEAT_POWER: /* fake port always on */ - /* According to USB2.0[11.24.2.7.1.4], no need to implement? */ - case USB_PORT_FEAT_C_OVER_CURRENT: - break; - case USB_PORT_FEAT_C_RESET: - wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET; - break; - case USB_PORT_FEAT_C_CONNECTION: - wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION; - break; - case USB_PORT_FEAT_ENABLE: - __wusbhc_dev_disable(wusbhc, port_idx); - break; - case USB_PORT_FEAT_C_ENABLE: - wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE; - break; - case USB_PORT_FEAT_SUSPEND: - case USB_PORT_FEAT_C_SUSPEND: - dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n", - port_idx, feature, selector); - result = -ENOSYS; - break; - default: - dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n", - port_idx, feature, selector); - result = -EPIPE; - break; - } - mutex_unlock(&wusbhc->mutex); - - return result; -} - -/* - * Return the port's status - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. - */ -static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx, - u32 *_buf, u16 wLength) -{ - __le16 *buf = (__le16 *)_buf; - - if (port_idx > wusbhc->ports_max) - return -EINVAL; - - mutex_lock(&wusbhc->mutex); - buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status); - buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change); - mutex_unlock(&wusbhc->mutex); - - return 0; -} - -/* - * Entry point for Root Hub operations - * - * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. - */ -int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue, - u16 wIndex, char *buf, u16 wLength) -{ - int result = -ENOSYS; - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - - switch (reqntype) { - case GetHubDescriptor: - result = wusbhc_rh_get_hub_descr( - wusbhc, wValue, wIndex, - (struct usb_hub_descriptor *) buf, wLength); - break; - case ClearHubFeature: - result = wusbhc_rh_clear_hub_feat(wusbhc, wValue); - break; - case GetHubStatus: - result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength); - break; - - case SetPortFeature: - result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8, - (wIndex & 0xff) - 1); - break; - case ClearPortFeature: - result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8, - (wIndex & 0xff) - 1); - break; - case GetPortStatus: - result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1, - (u32 *)buf, wLength); - break; - - case SetHubFeature: - default: - dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) " - "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype, - wValue, wIndex, buf, wLength); - /* dump_stack(); */ - result = -ENOSYS; - } - return result; -} -EXPORT_SYMBOL_GPL(wusbhc_rh_control); - -int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx) -{ - struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); - dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n", - __func__, usb_hcd, wusbhc, port_idx); - WARN_ON(1); - return -ENOSYS; -} -EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset); - -static void wusb_port_init(struct wusb_port *port) -{ - port->status |= USB_PORT_STAT_HIGH_SPEED; -} - -/* - * Alloc fake port specific fields and status. - */ -int wusbhc_rh_create(struct wusbhc *wusbhc) -{ - int result = -ENOMEM; - size_t port_size, itr; - port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]); - wusbhc->port = kzalloc(port_size, GFP_KERNEL); - if (wusbhc->port == NULL) - goto error_port_alloc; - for (itr = 0; itr < wusbhc->ports_max; itr++) - wusb_port_init(&wusbhc->port[itr]); - result = 0; -error_port_alloc: - return result; -} - -void wusbhc_rh_destroy(struct wusbhc *wusbhc) -{ - kfree(wusbhc->port); -} diff --git a/drivers/staging/wusbcore/security.c b/drivers/staging/wusbcore/security.c deleted file mode 100644 index 14ac8c98ac9e..000000000000 --- a/drivers/staging/wusbcore/security.c +++ /dev/null @@ -1,599 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB Host Controller - * Security support: encryption enablement, etc - * - * Copyright (C) 2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - */ -#include -#include -#include -#include -#include -#include "wusbhc.h" -#include - -static void wusbhc_gtk_rekey_work(struct work_struct *work); - -int wusbhc_sec_create(struct wusbhc *wusbhc) -{ - /* - * WQ is singlethread because we need to serialize rekey operations. - * Use a separate workqueue for security operations instead of the - * wusbd workqueue because security operations may need to communicate - * directly with downstream wireless devices using synchronous URBs. - * If a device is not responding, this could block other host - * controller operations. - */ - wusbhc->wq_security = create_singlethread_workqueue("wusbd_security"); - if (wusbhc->wq_security == NULL) { - pr_err("WUSB-core: Cannot create wusbd_security workqueue\n"); - return -ENOMEM; - } - - wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + - sizeof(wusbhc->gtk.data); - wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY; - wusbhc->gtk.descr.bReserved = 0; - wusbhc->gtk_index = 0; - - INIT_WORK(&wusbhc->gtk_rekey_work, wusbhc_gtk_rekey_work); - - return 0; -} - - -/* Called when the HC is destroyed */ -void wusbhc_sec_destroy(struct wusbhc *wusbhc) -{ - destroy_workqueue(wusbhc->wq_security); -} - - -/** - * wusbhc_next_tkid - generate a new, currently unused, TKID - * @wusbhc: the WUSB host controller - * @wusb_dev: the device whose PTK the TKID is for - * (or NULL for a TKID for a GTK) - * - * The generated TKID consists of two parts: the device's authenticated - * address (or 0 or a GTK); and an incrementing number. This ensures - * that TKIDs cannot be shared between devices and by the time the - * incrementing number wraps around the older TKIDs will no longer be - * in use (a maximum of two keys may be active at any one time). - */ -static u32 wusbhc_next_tkid(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) -{ - u32 *tkid; - u32 addr; - - if (wusb_dev == NULL) { - tkid = &wusbhc->gtk_tkid; - addr = 0; - } else { - tkid = &wusb_port_by_idx(wusbhc, wusb_dev->port_idx)->ptk_tkid; - addr = wusb_dev->addr & 0x7f; - } - - *tkid = (addr << 8) | ((*tkid + 1) & 0xff); - - return *tkid; -} - -static void wusbhc_generate_gtk(struct wusbhc *wusbhc) -{ - const size_t key_size = sizeof(wusbhc->gtk.data); - u32 tkid; - - tkid = wusbhc_next_tkid(wusbhc, NULL); - - wusbhc->gtk.descr.tTKID[0] = (tkid >> 0) & 0xff; - wusbhc->gtk.descr.tTKID[1] = (tkid >> 8) & 0xff; - wusbhc->gtk.descr.tTKID[2] = (tkid >> 16) & 0xff; - - get_random_bytes(wusbhc->gtk.descr.bKeyData, key_size); -} - -/** - * wusbhc_sec_start - start the security management process - * @wusbhc: the WUSB host controller - * - * Generate and set an initial GTK on the host controller. - * - * Called when the HC is started. - */ -int wusbhc_sec_start(struct wusbhc *wusbhc) -{ - const size_t key_size = sizeof(wusbhc->gtk.data); - int result; - - wusbhc_generate_gtk(wusbhc); - - result = wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, - &wusbhc->gtk.descr.bKeyData, key_size); - if (result < 0) - dev_err(wusbhc->dev, "cannot set GTK for the host: %d\n", - result); - - return result; -} - -/** - * wusbhc_sec_stop - stop the security management process - * @wusbhc: the WUSB host controller - * - * Wait for any pending GTK rekeys to stop. - */ -void wusbhc_sec_stop(struct wusbhc *wusbhc) -{ - cancel_work_sync(&wusbhc->gtk_rekey_work); -} - - -/** @returns encryption type name */ -const char *wusb_et_name(u8 x) -{ - switch (x) { - case USB_ENC_TYPE_UNSECURE: return "unsecure"; - case USB_ENC_TYPE_WIRED: return "wired"; - case USB_ENC_TYPE_CCM_1: return "CCM-1"; - case USB_ENC_TYPE_RSA_1: return "RSA-1"; - default: return "unknown"; - } -} -EXPORT_SYMBOL_GPL(wusb_et_name); - -/* - * Set the device encryption method - * - * We tell the device which encryption method to use; we do this when - * setting up the device's security. - */ -static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value) -{ - int result; - struct device *dev = &usb_dev->dev; - struct wusb_dev *wusb_dev = usb_dev->wusb_dev; - - if (value) { - value = wusb_dev->ccm1_etd.bEncryptionValue; - } else { - /* FIXME: should be wusb_dev->etd[UNSECURE].bEncryptionValue */ - value = 0; - } - /* Set device's */ - result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_ENCRYPTION, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - value, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (result < 0) - dev_err(dev, "Can't set device's WUSB encryption to " - "%s (value %d): %d\n", - wusb_et_name(wusb_dev->ccm1_etd.bEncryptionType), - wusb_dev->ccm1_etd.bEncryptionValue, result); - return result; -} - -/* - * Set the GTK to be used by a device. - * - * The device must be authenticated. - */ -static int wusb_dev_set_gtk(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) -{ - struct usb_device *usb_dev = wusb_dev->usb_dev; - u8 key_index = wusb_key_index(wusbhc->gtk_index, - WUSB_KEY_INDEX_TYPE_GTK, WUSB_KEY_INDEX_ORIGINATOR_HOST); - - return usb_control_msg( - usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_DESCRIPTOR, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - USB_DT_KEY << 8 | key_index, 0, - &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength, - USB_CTRL_SET_TIMEOUT); -} - - -/* FIXME: prototype for adding security */ -int wusb_dev_sec_add(struct wusbhc *wusbhc, - struct usb_device *usb_dev, struct wusb_dev *wusb_dev) -{ - int result, bytes, secd_size; - struct device *dev = &usb_dev->dev; - struct usb_security_descriptor *secd, *new_secd; - const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL; - const void *itr, *top; - char buf[64]; - - secd = kmalloc(sizeof(*secd), GFP_KERNEL); - if (secd == NULL) { - result = -ENOMEM; - goto out; - } - - result = usb_get_descriptor(usb_dev, USB_DT_SECURITY, - 0, secd, sizeof(*secd)); - if (result < (int)sizeof(*secd)) { - dev_err(dev, "Can't read security descriptor or " - "not enough data: %d\n", result); - goto out; - } - secd_size = le16_to_cpu(secd->wTotalLength); - new_secd = krealloc(secd, secd_size, GFP_KERNEL); - if (new_secd == NULL) { - dev_err(dev, - "Can't allocate space for security descriptors\n"); - result = -ENOMEM; - goto out; - } - secd = new_secd; - result = usb_get_descriptor(usb_dev, USB_DT_SECURITY, - 0, secd, secd_size); - if (result < secd_size) { - dev_err(dev, "Can't read security descriptor or " - "not enough data: %d\n", result); - goto out; - } - bytes = 0; - itr = &secd[1]; - top = (void *)secd + result; - while (itr < top) { - etd = itr; - if (top - itr < sizeof(*etd)) { - dev_err(dev, "BUG: bad device security descriptor; " - "not enough data (%zu vs %zu bytes left)\n", - top - itr, sizeof(*etd)); - break; - } - if (etd->bLength < sizeof(*etd)) { - dev_err(dev, "BUG: bad device encryption descriptor; " - "descriptor is too short " - "(%u vs %zu needed)\n", - etd->bLength, sizeof(*etd)); - break; - } - itr += etd->bLength; - bytes += snprintf(buf + bytes, sizeof(buf) - bytes, - "%s (0x%02x/%02x) ", - wusb_et_name(etd->bEncryptionType), - etd->bEncryptionValue, etd->bAuthKeyIndex); - if (etd->bEncryptionType == USB_ENC_TYPE_CCM_1) - ccm1_etd = etd; - } - /* This code only supports CCM1 as of now. */ - /* FIXME: user has to choose which sec mode to use? - * In theory we want CCM */ - if (ccm1_etd == NULL) { - dev_err(dev, "WUSB device doesn't support CCM1 encryption, " - "can't use!\n"); - result = -EINVAL; - goto out; - } - wusb_dev->ccm1_etd = *ccm1_etd; - dev_dbg(dev, "supported encryption: %s; using %s (0x%02x/%02x)\n", - buf, wusb_et_name(ccm1_etd->bEncryptionType), - ccm1_etd->bEncryptionValue, ccm1_etd->bAuthKeyIndex); - result = 0; -out: - kfree(secd); - return result; -} - -void wusb_dev_sec_rm(struct wusb_dev *wusb_dev) -{ - /* Nothing so far */ -} - -/** - * Update the address of an unauthenticated WUSB device - * - * Once we have successfully authenticated, we take it to addr0 state - * and then to a normal address. - * - * Before the device's address (as known by it) was usb_dev->devnum | - * 0x80 (unauthenticated address). With this we update it to usb_dev->devnum. - */ -int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) -{ - int result = -ENOMEM; - struct usb_device *usb_dev = wusb_dev->usb_dev; - struct device *dev = &usb_dev->dev; - u8 new_address = wusb_dev->addr & 0x7F; - - /* Set address 0 */ - result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_ADDRESS, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (result < 0) { - dev_err(dev, "auth failed: can't set address 0: %d\n", - result); - goto error_addr0; - } - result = wusb_set_dev_addr(wusbhc, wusb_dev, 0); - if (result < 0) - goto error_addr0; - usb_set_device_state(usb_dev, USB_STATE_DEFAULT); - usb_ep0_reinit(usb_dev); - - /* Set new (authenticated) address. */ - result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_ADDRESS, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - new_address, 0, NULL, 0, - USB_CTRL_SET_TIMEOUT); - if (result < 0) { - dev_err(dev, "auth failed: can't set address %u: %d\n", - new_address, result); - goto error_addr; - } - result = wusb_set_dev_addr(wusbhc, wusb_dev, new_address); - if (result < 0) - goto error_addr; - usb_set_device_state(usb_dev, USB_STATE_ADDRESS); - usb_ep0_reinit(usb_dev); - usb_dev->authenticated = 1; -error_addr: -error_addr0: - return result; -} - -/* - * - * - */ -/* FIXME: split and cleanup */ -int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, - struct wusb_ckhdid *ck) -{ - int result = -ENOMEM; - struct usb_device *usb_dev = wusb_dev->usb_dev; - struct device *dev = &usb_dev->dev; - u32 tkid; - struct usb_handshake *hs; - struct aes_ccm_nonce ccm_n; - u8 mic[8]; - struct wusb_keydvt_in keydvt_in; - struct wusb_keydvt_out keydvt_out; - - hs = kcalloc(3, sizeof(hs[0]), GFP_KERNEL); - if (!hs) - goto error_kzalloc; - - /* We need to turn encryption before beginning the 4way - * hshake (WUSB1.0[.3.2.2]) */ - result = wusb_dev_set_encryption(usb_dev, 1); - if (result < 0) - goto error_dev_set_encryption; - - tkid = wusbhc_next_tkid(wusbhc, wusb_dev); - - hs[0].bMessageNumber = 1; - hs[0].bStatus = 0; - put_unaligned_le32(tkid, hs[0].tTKID); - hs[0].bReserved = 0; - memcpy(hs[0].CDID, &wusb_dev->cdid, sizeof(hs[0].CDID)); - get_random_bytes(&hs[0].nonce, sizeof(hs[0].nonce)); - memset(hs[0].MIC, 0, sizeof(hs[0].MIC)); /* Per WUSB1.0[T7-22] */ - - result = usb_control_msg( - usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_HANDSHAKE, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - 1, 0, &hs[0], sizeof(hs[0]), USB_CTRL_SET_TIMEOUT); - if (result < 0) { - dev_err(dev, "Handshake1: request failed: %d\n", result); - goto error_hs1; - } - - /* Handshake 2, from the device -- need to verify fields */ - result = usb_control_msg( - usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_GET_HANDSHAKE, - USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - 2, 0, &hs[1], sizeof(hs[1]), USB_CTRL_GET_TIMEOUT); - if (result < 0) { - dev_err(dev, "Handshake2: request failed: %d\n", result); - goto error_hs2; - } - - result = -EINVAL; - if (hs[1].bMessageNumber != 2) { - dev_err(dev, "Handshake2 failed: bad message number %u\n", - hs[1].bMessageNumber); - goto error_hs2; - } - if (hs[1].bStatus != 0) { - dev_err(dev, "Handshake2 failed: bad status %u\n", - hs[1].bStatus); - goto error_hs2; - } - if (memcmp(hs[0].tTKID, hs[1].tTKID, sizeof(hs[0].tTKID))) { - dev_err(dev, "Handshake2 failed: TKID mismatch " - "(#1 0x%02x%02x%02x vs #2 0x%02x%02x%02x)\n", - hs[0].tTKID[0], hs[0].tTKID[1], hs[0].tTKID[2], - hs[1].tTKID[0], hs[1].tTKID[1], hs[1].tTKID[2]); - goto error_hs2; - } - if (memcmp(hs[0].CDID, hs[1].CDID, sizeof(hs[0].CDID))) { - dev_err(dev, "Handshake2 failed: CDID mismatch\n"); - goto error_hs2; - } - - /* Setup the CCM nonce */ - memset(&ccm_n.sfn, 0, sizeof(ccm_n.sfn)); /* Per WUSB1.0[6.5.2] */ - put_unaligned_le32(tkid, ccm_n.tkid); - ccm_n.src_addr = wusbhc->uwb_rc->uwb_dev.dev_addr; - ccm_n.dest_addr.data[0] = wusb_dev->addr; - ccm_n.dest_addr.data[1] = 0; - - /* Derive the KCK and PTK from CK, the CCM, H and D nonces */ - memcpy(keydvt_in.hnonce, hs[0].nonce, sizeof(keydvt_in.hnonce)); - memcpy(keydvt_in.dnonce, hs[1].nonce, sizeof(keydvt_in.dnonce)); - result = wusb_key_derive(&keydvt_out, ck->data, &ccm_n, &keydvt_in); - if (result < 0) { - dev_err(dev, "Handshake2 failed: cannot derive keys: %d\n", - result); - goto error_hs2; - } - - /* Compute MIC and verify it */ - result = wusb_oob_mic(mic, keydvt_out.kck, &ccm_n, &hs[1]); - if (result < 0) { - dev_err(dev, "Handshake2 failed: cannot compute MIC: %d\n", - result); - goto error_hs2; - } - - if (memcmp(hs[1].MIC, mic, sizeof(hs[1].MIC))) { - dev_err(dev, "Handshake2 failed: MIC mismatch\n"); - goto error_hs2; - } - - /* Send Handshake3 */ - hs[2].bMessageNumber = 3; - hs[2].bStatus = 0; - put_unaligned_le32(tkid, hs[2].tTKID); - hs[2].bReserved = 0; - memcpy(hs[2].CDID, &wusb_dev->cdid, sizeof(hs[2].CDID)); - memcpy(hs[2].nonce, hs[0].nonce, sizeof(hs[2].nonce)); - result = wusb_oob_mic(hs[2].MIC, keydvt_out.kck, &ccm_n, &hs[2]); - if (result < 0) { - dev_err(dev, "Handshake3 failed: cannot compute MIC: %d\n", - result); - goto error_hs2; - } - - result = usb_control_msg( - usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_HANDSHAKE, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - 3, 0, &hs[2], sizeof(hs[2]), USB_CTRL_SET_TIMEOUT); - if (result < 0) { - dev_err(dev, "Handshake3: request failed: %d\n", result); - goto error_hs3; - } - - result = wusbhc->set_ptk(wusbhc, wusb_dev->port_idx, tkid, - keydvt_out.ptk, sizeof(keydvt_out.ptk)); - if (result < 0) - goto error_wusbhc_set_ptk; - - result = wusb_dev_set_gtk(wusbhc, wusb_dev); - if (result < 0) { - dev_err(dev, "Set GTK for device: request failed: %d\n", - result); - goto error_wusbhc_set_gtk; - } - - /* Update the device's address from unauth to auth */ - if (usb_dev->authenticated == 0) { - result = wusb_dev_update_address(wusbhc, wusb_dev); - if (result < 0) - goto error_dev_update_address; - } - result = 0; - dev_info(dev, "device authenticated\n"); - -error_dev_update_address: -error_wusbhc_set_gtk: -error_wusbhc_set_ptk: -error_hs3: -error_hs2: -error_hs1: - memset(hs, 0, 3*sizeof(hs[0])); - memzero_explicit(&keydvt_out, sizeof(keydvt_out)); - memzero_explicit(&keydvt_in, sizeof(keydvt_in)); - memzero_explicit(&ccm_n, sizeof(ccm_n)); - memzero_explicit(mic, sizeof(mic)); - if (result < 0) - wusb_dev_set_encryption(usb_dev, 0); -error_dev_set_encryption: - kfree(hs); -error_kzalloc: - return result; -} - -/* - * Once all connected and authenticated devices have received the new - * GTK, switch the host to using it. - */ -static void wusbhc_gtk_rekey_work(struct work_struct *work) -{ - struct wusbhc *wusbhc = container_of(work, - struct wusbhc, gtk_rekey_work); - size_t key_size = sizeof(wusbhc->gtk.data); - int port_idx; - struct wusb_dev *wusb_dev, *wusb_dev_next; - LIST_HEAD(rekey_list); - - mutex_lock(&wusbhc->mutex); - /* generate the new key */ - wusbhc_generate_gtk(wusbhc); - /* roll the gtk index. */ - wusbhc->gtk_index = (wusbhc->gtk_index + 1) % (WUSB_KEY_INDEX_MAX + 1); - /* - * Save all connected devices on a list while holding wusbhc->mutex and - * take a reference to each one. Then submit the set key request to - * them after releasing the lock in order to avoid a deadlock. - */ - for (port_idx = 0; port_idx < wusbhc->ports_max; port_idx++) { - wusb_dev = wusbhc->port[port_idx].wusb_dev; - if (!wusb_dev || !wusb_dev->usb_dev - || !wusb_dev->usb_dev->authenticated) - continue; - - wusb_dev_get(wusb_dev); - list_add_tail(&wusb_dev->rekey_node, &rekey_list); - } - mutex_unlock(&wusbhc->mutex); - - /* Submit the rekey requests without holding wusbhc->mutex. */ - list_for_each_entry_safe(wusb_dev, wusb_dev_next, &rekey_list, - rekey_node) { - list_del_init(&wusb_dev->rekey_node); - dev_dbg(&wusb_dev->usb_dev->dev, - "%s: rekey device at port %d\n", - __func__, wusb_dev->port_idx); - - if (wusb_dev_set_gtk(wusbhc, wusb_dev) < 0) { - dev_err(&wusb_dev->usb_dev->dev, - "%s: rekey device at port %d failed\n", - __func__, wusb_dev->port_idx); - } - wusb_dev_put(wusb_dev); - } - - /* Switch the host controller to use the new GTK. */ - mutex_lock(&wusbhc->mutex); - wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, - &wusbhc->gtk.descr.bKeyData, key_size); - mutex_unlock(&wusbhc->mutex); -} - -/** - * wusbhc_gtk_rekey - generate and distribute a new GTK - * @wusbhc: the WUSB host controller - * - * Generate a new GTK and distribute it to all connected and - * authenticated devices. When all devices have the new GTK, the host - * starts using it. - * - * This must be called after every device disconnect (see [WUSB] - * section 6.2.11.2). - */ -void wusbhc_gtk_rekey(struct wusbhc *wusbhc) -{ - /* - * We need to submit a URB to the downstream WUSB devices in order to - * change the group key. This can't be done while holding the - * wusbhc->mutex since that is also taken in the urb_enqueue routine - * and will cause a deadlock. Instead, queue a work item to do - * it when the lock is not held - */ - queue_work(wusbhc->wq_security, &wusbhc->gtk_rekey_work); -} diff --git a/drivers/staging/wusbcore/wa-hc.c b/drivers/staging/wusbcore/wa-hc.c deleted file mode 100644 index 6827075fb8a1..000000000000 --- a/drivers/staging/wusbcore/wa-hc.c +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wire Adapter Host Controller Driver - * Common items to HWA and DWA based HCDs - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - */ -#include -#include -#include "wusbhc.h" -#include "wa-hc.h" - -/** - * Assumes - * - * wa->usb_dev and wa->usb_iface initialized and refcounted, - * wa->wa_descr initialized. - */ -int wa_create(struct wahc *wa, struct usb_interface *iface, - kernel_ulong_t quirks) -{ - int result; - struct device *dev = &iface->dev; - - if (iface->cur_altsetting->desc.bNumEndpoints < 3) - return -ENODEV; - - result = wa_rpipes_create(wa); - if (result < 0) - goto error_rpipes_create; - wa->quirks = quirks; - /* Fill up Data Transfer EP pointers */ - wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc; - wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc; - wa->dti_buf_size = usb_endpoint_maxp(wa->dti_epd); - wa->dti_buf = kmalloc(wa->dti_buf_size, GFP_KERNEL); - if (wa->dti_buf == NULL) { - result = -ENOMEM; - goto error_dti_buf_alloc; - } - result = wa_nep_create(wa, iface); - if (result < 0) { - dev_err(dev, "WA-CDS: can't initialize notif endpoint: %d\n", - result); - goto error_nep_create; - } - return 0; - -error_nep_create: - kfree(wa->dti_buf); -error_dti_buf_alloc: - wa_rpipes_destroy(wa); -error_rpipes_create: - return result; -} -EXPORT_SYMBOL_GPL(wa_create); - - -void __wa_destroy(struct wahc *wa) -{ - if (wa->dti_urb) { - usb_kill_urb(wa->dti_urb); - usb_put_urb(wa->dti_urb); - } - kfree(wa->dti_buf); - wa_nep_destroy(wa); - wa_rpipes_destroy(wa); -} -EXPORT_SYMBOL_GPL(__wa_destroy); - -/** - * wa_reset_all - reset the WA device - * @wa: the WA to be reset - * - * For HWAs the radio controller and all other PALs are also reset. - */ -void wa_reset_all(struct wahc *wa) -{ - /* FIXME: assuming HWA. */ - wusbhc_reset_all(wa->wusb); -} - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("Wireless USB Wire Adapter core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/wusbcore/wa-hc.h b/drivers/staging/wusbcore/wa-hc.h deleted file mode 100644 index 5a38465724c2..000000000000 --- a/drivers/staging/wusbcore/wa-hc.h +++ /dev/null @@ -1,467 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * HWA Host Controller Driver - * Wire Adapter Control/Data Streaming Iface (WUSB1.0[8]) - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This driver implements a USB Host Controller (struct usb_hcd) for a - * Wireless USB Host Controller based on the Wireless USB 1.0 - * Host-Wire-Adapter specification (in layman terms, a USB-dongle that - * implements a Wireless USB host). - * - * Check out the Design-overview.txt file in the source documentation - * for other details on the implementation. - * - * Main blocks: - * - * driver glue with the driver API, workqueue daemon - * - * lc RC instance life cycle management (create, destroy...) - * - * hcd glue with the USB API Host Controller Interface API. - * - * nep Notification EndPoint management: collect notifications - * and queue them with the workqueue daemon. - * - * Handle notifications as coming from the NEP. Sends them - * off others to their respective modules (eg: connect, - * disconnect and reset go to devconnect). - * - * rpipe Remote Pipe management; rpipe is what we use to write - * to an endpoint on a WUSB device that is connected to a - * HWA RC. - * - * xfer Transfer management -- this is all the code that gets a - * buffer and pushes it to a device (or viceversa). * - * - * Some day a lot of this code will be shared between this driver and - * the drivers for DWA (xfer, rpipe). - * - * All starts at driver.c:hwahc_probe(), when one of this guys is - * connected. hwahc_disconnect() stops it. - * - * During operation, the main driver is devices connecting or - * disconnecting. They cause the HWA RC to send notifications into - * nep.c:hwahc_nep_cb() that will dispatch them to - * notif.c:wa_notif_dispatch(). From there they will fan to cause - * device connects, disconnects, etc. - * - * Note much of the activity is difficult to follow. For example a - * device connect goes to devconnect, which will cause the "fake" root - * hub port to show a connect and stop there. Then hub_wq will notice - * and call into the rh.c:hwahc_rc_port_reset() code to authenticate - * the device (and this might require user intervention) and enable - * the port. - * - * We also have a timer workqueue going from devconnect.c that - * schedules in hwahc_devconnect_create(). - * - * The rest of the traffic is in the usual entry points of a USB HCD, - * which are hooked up in driver.c:hwahc_rc_driver, and defined in - * hcd.c. - */ - -#ifndef __HWAHC_INTERNAL_H__ -#define __HWAHC_INTERNAL_H__ - -#include -#include -#include -#include -#include "../uwb/uwb.h" -#include "include/wusb.h" -#include "include/wusb-wa.h" - -struct wusbhc; -struct wahc; -extern void wa_urb_enqueue_run(struct work_struct *ws); -extern void wa_process_errored_transfers_run(struct work_struct *ws); - -/** - * RPipe instance - * - * @descr's fields are kept in LE, as we need to send it back and - * forth. - * - * @wa is referenced when set - * - * @segs_available is the number of requests segments that still can - * be submitted to the controller without overloading - * it. It is initialized to descr->wRequests when - * aiming. - * - * A rpipe supports a max of descr->wRequests at the same time; before - * submitting seg_lock has to be taken. If segs_avail > 0, then we can - * submit; if not, we have to queue them. - */ -struct wa_rpipe { - struct kref refcnt; - struct usb_rpipe_descriptor descr; - struct usb_host_endpoint *ep; - struct wahc *wa; - spinlock_t seg_lock; - struct list_head seg_list; - struct list_head list_node; - atomic_t segs_available; - u8 buffer[1]; /* For reads/writes on USB */ -}; - - -enum wa_dti_state { - WA_DTI_TRANSFER_RESULT_PENDING, - WA_DTI_ISOC_PACKET_STATUS_PENDING, - WA_DTI_BUF_IN_DATA_PENDING -}; - -enum wa_quirks { - /* - * The Alereon HWA expects the data frames in isochronous transfer - * requests to be concatenated and not sent as separate packets. - */ - WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC = 0x01, - /* - * The Alereon HWA can be instructed to not send transfer notifications - * as an optimization. - */ - WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS = 0x02, -}; - -enum wa_vendor_specific_requests { - WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS = 0x4C, - WA_REQ_ALEREON_FEATURE_SET = 0x01, - WA_REQ_ALEREON_FEATURE_CLEAR = 0x00, -}; - -#define WA_MAX_BUF_IN_URBS 4 -/** - * Instance of a HWA Host Controller - * - * Except where a more specific lock/mutex applies or atomic, all - * fields protected by @mutex. - * - * @wa_descr Can be accessed without locking because it is in - * the same area where the device descriptors were - * read, so it is guaranteed to exist unmodified while - * the device exists. - * - * Endianess has been converted to CPU's. - * - * @nep_* can be accessed without locking as its processing is - * serialized; we submit a NEP URB and it comes to - * hwahc_nep_cb(), which won't issue another URB until it is - * done processing it. - * - * @xfer_list: - * - * List of active transfers to verify existence from a xfer id - * gotten from the xfer result message. Can't use urb->list because - * it goes by endpoint, and we don't know the endpoint at the time - * when we get the xfer result message. We can't really rely on the - * pointer (will have to change for 64 bits) as the xfer id is 32 bits. - * - * @xfer_delayed_list: List of transfers that need to be started - * (with a workqueue, because they were - * submitted from an atomic context). - * - * FIXME: this needs to be layered up: a wusbhc layer (for sharing - * commonalities with WHCI), a wa layer (for sharing - * commonalities with DWA-RC). - */ -struct wahc { - struct usb_device *usb_dev; - struct usb_interface *usb_iface; - - /* HC to deliver notifications */ - union { - struct wusbhc *wusb; - struct dwahc *dwa; - }; - - const struct usb_endpoint_descriptor *dto_epd, *dti_epd; - const struct usb_wa_descriptor *wa_descr; - - struct urb *nep_urb; /* Notification EndPoint [lockless] */ - struct edc nep_edc; - void *nep_buffer; - size_t nep_buffer_size; - - atomic_t notifs_queued; - - u16 rpipes; - unsigned long *rpipe_bm; /* rpipe usage bitmap */ - struct list_head rpipe_delayed_list; /* delayed RPIPES. */ - spinlock_t rpipe_lock; /* protect rpipe_bm and delayed list */ - struct mutex rpipe_mutex; /* assigning resources to endpoints */ - - /* - * dti_state is used to track the state of the dti_urb. When dti_state - * is WA_DTI_ISOC_PACKET_STATUS_PENDING, dti_isoc_xfer_in_progress and - * dti_isoc_xfer_seg identify which xfer the incoming isoc packet - * status refers to. - */ - enum wa_dti_state dti_state; - u32 dti_isoc_xfer_in_progress; - u8 dti_isoc_xfer_seg; - struct urb *dti_urb; /* URB for reading xfer results */ - /* URBs for reading data in */ - struct urb buf_in_urbs[WA_MAX_BUF_IN_URBS]; - int active_buf_in_urbs; /* number of buf_in_urbs active. */ - struct edc dti_edc; /* DTI error density counter */ - void *dti_buf; - size_t dti_buf_size; - - unsigned long dto_in_use; /* protect dto endoint serialization */ - - s32 status; /* For reading status */ - - struct list_head xfer_list; - struct list_head xfer_delayed_list; - struct list_head xfer_errored_list; - /* - * lock for the above xfer lists. Can be taken while a xfer->lock is - * held but not in the reverse order. - */ - spinlock_t xfer_list_lock; - struct work_struct xfer_enqueue_work; - struct work_struct xfer_error_work; - atomic_t xfer_id_count; - - kernel_ulong_t quirks; -}; - - -extern int wa_create(struct wahc *wa, struct usb_interface *iface, - kernel_ulong_t); -extern void __wa_destroy(struct wahc *wa); -extern int wa_dti_start(struct wahc *wa); -void wa_reset_all(struct wahc *wa); - - -/* Miscellaneous constants */ -enum { - /** Max number of EPROTO errors we tolerate on the NEP in a - * period of time */ - HWAHC_EPROTO_MAX = 16, - /** Period of time for EPROTO errors (in jiffies) */ - HWAHC_EPROTO_PERIOD = 4 * HZ, -}; - - -/* Notification endpoint handling */ -extern int wa_nep_create(struct wahc *, struct usb_interface *); -extern void wa_nep_destroy(struct wahc *); - -static inline int wa_nep_arm(struct wahc *wa, gfp_t gfp_mask) -{ - struct urb *urb = wa->nep_urb; - urb->transfer_buffer = wa->nep_buffer; - urb->transfer_buffer_length = wa->nep_buffer_size; - return usb_submit_urb(urb, gfp_mask); -} - -static inline void wa_nep_disarm(struct wahc *wa) -{ - usb_kill_urb(wa->nep_urb); -} - - -/* RPipes */ -static inline void wa_rpipe_init(struct wahc *wa) -{ - INIT_LIST_HEAD(&wa->rpipe_delayed_list); - spin_lock_init(&wa->rpipe_lock); - mutex_init(&wa->rpipe_mutex); -} - -static inline void wa_init(struct wahc *wa) -{ - int index; - - edc_init(&wa->nep_edc); - atomic_set(&wa->notifs_queued, 0); - wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; - wa_rpipe_init(wa); - edc_init(&wa->dti_edc); - INIT_LIST_HEAD(&wa->xfer_list); - INIT_LIST_HEAD(&wa->xfer_delayed_list); - INIT_LIST_HEAD(&wa->xfer_errored_list); - spin_lock_init(&wa->xfer_list_lock); - INIT_WORK(&wa->xfer_enqueue_work, wa_urb_enqueue_run); - INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run); - wa->dto_in_use = 0; - atomic_set(&wa->xfer_id_count, 1); - /* init the buf in URBs */ - for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index) - usb_init_urb(&(wa->buf_in_urbs[index])); - wa->active_buf_in_urbs = 0; -} - -/** - * Destroy a pipe (when refcount drops to zero) - * - * Assumes it has been moved to the "QUIESCING" state. - */ -struct wa_xfer; -extern void rpipe_destroy(struct kref *_rpipe); -static inline -void __rpipe_get(struct wa_rpipe *rpipe) -{ - kref_get(&rpipe->refcnt); -} -extern int rpipe_get_by_ep(struct wahc *, struct usb_host_endpoint *, - struct urb *, gfp_t); -static inline void rpipe_put(struct wa_rpipe *rpipe) -{ - kref_put(&rpipe->refcnt, rpipe_destroy); - -} -extern void rpipe_ep_disable(struct wahc *, struct usb_host_endpoint *); -extern void rpipe_clear_feature_stalled(struct wahc *, - struct usb_host_endpoint *); -extern int wa_rpipes_create(struct wahc *); -extern void wa_rpipes_destroy(struct wahc *); -static inline void rpipe_avail_dec(struct wa_rpipe *rpipe) -{ - atomic_dec(&rpipe->segs_available); -} - -/** - * Returns true if the rpipe is ready to submit more segments. - */ -static inline int rpipe_avail_inc(struct wa_rpipe *rpipe) -{ - return atomic_inc_return(&rpipe->segs_available) > 0 - && !list_empty(&rpipe->seg_list); -} - - -/* Transferring data */ -extern int wa_urb_enqueue(struct wahc *, struct usb_host_endpoint *, - struct urb *, gfp_t); -extern int wa_urb_dequeue(struct wahc *, struct urb *, int); -extern void wa_handle_notif_xfer(struct wahc *, struct wa_notif_hdr *); - - -/* Misc - * - * FIXME: Refcounting for the actual @hwahc object is not correct; I - * mean, this should be refcounting on the HCD underneath, but - * it is not. In any case, the semantics for HCD refcounting - * are *weird*...on refcount reaching zero it just frees - * it...no RC specific function is called...unless I miss - * something. - * - * FIXME: has to go away in favour of a 'struct' hcd based solution - */ -static inline struct wahc *wa_get(struct wahc *wa) -{ - usb_get_intf(wa->usb_iface); - return wa; -} - -static inline void wa_put(struct wahc *wa) -{ - usb_put_intf(wa->usb_iface); -} - - -static inline int __wa_feature(struct wahc *wa, unsigned op, u16 feature) -{ - return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - op ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - feature, - wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, USB_CTRL_SET_TIMEOUT); -} - - -static inline int __wa_set_feature(struct wahc *wa, u16 feature) -{ - return __wa_feature(wa, 1, feature); -} - - -static inline int __wa_clear_feature(struct wahc *wa, u16 feature) -{ - return __wa_feature(wa, 0, feature); -} - - -/** - * Return the status of a Wire Adapter - * - * @wa: Wire Adapter instance - * @returns < 0 errno code on error, or status bitmap as described - * in WUSB1.0[8.3.1.6]. - * - * NOTE: need malloc, some arches don't take USB from the stack - */ -static inline -s32 __wa_get_status(struct wahc *wa) -{ - s32 result; - result = usb_control_msg( - wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), - USB_REQ_GET_STATUS, - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, - &wa->status, sizeof(wa->status), USB_CTRL_GET_TIMEOUT); - if (result >= 0) - result = wa->status; - return result; -} - - -/** - * Waits until the Wire Adapter's status matches @mask/@value - * - * @wa: Wire Adapter instance. - * @returns < 0 errno code on error, otherwise status. - * - * Loop until the WAs status matches the mask and value (status & mask - * == value). Timeout if it doesn't happen. - * - * FIXME: is there an official specification on how long status - * changes can take? - */ -static inline s32 __wa_wait_status(struct wahc *wa, u32 mask, u32 value) -{ - s32 result; - unsigned loops = 10; - do { - msleep(50); - result = __wa_get_status(wa); - if ((result & mask) == value) - break; - if (loops-- == 0) { - result = -ETIMEDOUT; - break; - } - } while (result >= 0); - return result; -} - - -/** Command @hwahc to stop, @returns 0 if ok, < 0 errno code on error */ -static inline int __wa_stop(struct wahc *wa) -{ - int result; - struct device *dev = &wa->usb_iface->dev; - - result = __wa_clear_feature(wa, WA_ENABLE); - if (result < 0 && result != -ENODEV) { - dev_err(dev, "error commanding HC to stop: %d\n", result); - goto out; - } - result = __wa_wait_status(wa, WA_ENABLE, 0); - if (result < 0 && result != -ENODEV) - dev_err(dev, "error waiting for HC to stop: %d\n", result); -out: - return 0; -} - - -#endif /* #ifndef __HWAHC_INTERNAL_H__ */ diff --git a/drivers/staging/wusbcore/wa-nep.c b/drivers/staging/wusbcore/wa-nep.c deleted file mode 100644 index 5f0656db5482..000000000000 --- a/drivers/staging/wusbcore/wa-nep.c +++ /dev/null @@ -1,289 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) - * Notification EndPoint support - * - * Copyright (C) 2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This part takes care of getting the notification from the hw - * only and dispatching through wusbwad into - * wa_notif_dispatch. Handling is done there. - * - * WA notifications are limited in size; most of them are three or - * four bytes long, and the longest is the HWA Device Notification, - * which would not exceed 38 bytes (DNs are limited in payload to 32 - * bytes plus 3 bytes header (WUSB1.0[7.6p2]), plus 3 bytes HWA - * header (WUSB1.0[8.5.4.2]). - * - * It is not clear if more than one Device Notification can be packed - * in a HWA Notification, I assume no because of the wording in - * WUSB1.0[8.5.4.2]. In any case, the bigger any notification could - * get is 256 bytes (as the bLength field is a byte). - * - * So what we do is we have this buffer and read into it; when a - * notification arrives we schedule work to a specific, single thread - * workqueue (so notifications are serialized) and copy the - * notification data. After scheduling the work, we rearm the read from - * the notification endpoint. - * - * Entry points here are: - * - * wa_nep_[create|destroy]() To initialize/release this subsystem - * - * wa_nep_cb() Callback for the notification - * endpoint; when data is ready, this - * does the dispatching. - */ -#include -#include -#include - -#include "wa-hc.h" -#include "wusbhc.h" - -/* Structure for queueing notifications to the workqueue */ -struct wa_notif_work { - struct work_struct work; - struct wahc *wa; - size_t size; - u8 data[]; -}; - -/* - * Process incoming notifications from the WA's Notification EndPoint - * [the wuswad daemon, basically] - * - * @_nw: Pointer to a descriptor which has the pointer to the - * @wa, the size of the buffer and the work queue - * structure (so we can free all when done). - * @returns 0 if ok, < 0 errno code on error. - * - * All notifications follow the same format; they need to start with a - * 'struct wa_notif_hdr' header, so it is easy to parse through - * them. We just break the buffer in individual notifications (the - * standard doesn't say if it can be done or is forbidden, so we are - * cautious) and dispatch each. - * - * So the handling layers are is: - * - * WA specific notification (from NEP) - * Device Notification Received -> wa_handle_notif_dn() - * WUSB Device notification generic handling - * BPST Adjustment -> wa_handle_notif_bpst_adj() - * ... -> ... - * - * @wa has to be referenced - */ -static void wa_notif_dispatch(struct work_struct *ws) -{ - void *itr; - u8 missing = 0; - struct wa_notif_work *nw = container_of(ws, struct wa_notif_work, - work); - struct wahc *wa = nw->wa; - struct wa_notif_hdr *notif_hdr; - size_t size; - - struct device *dev = &wa->usb_iface->dev; - -#if 0 - /* FIXME: need to check for this??? */ - if (usb_hcd->state == HC_STATE_QUIESCING) /* Going down? */ - goto out; /* screw it */ -#endif - atomic_dec(&wa->notifs_queued); /* Throttling ctl */ - size = nw->size; - itr = nw->data; - - while (size) { - if (size < sizeof(*notif_hdr)) { - missing = sizeof(*notif_hdr) - size; - goto exhausted_buffer; - } - notif_hdr = itr; - if (size < notif_hdr->bLength) - goto exhausted_buffer; - itr += notif_hdr->bLength; - size -= notif_hdr->bLength; - /* Dispatch the notification [don't use itr or size!] */ - switch (notif_hdr->bNotifyType) { - case HWA_NOTIF_DN: { - struct hwa_notif_dn *hwa_dn; - hwa_dn = container_of(notif_hdr, struct hwa_notif_dn, - hdr); - wusbhc_handle_dn(wa->wusb, hwa_dn->bSourceDeviceAddr, - hwa_dn->dndata, - notif_hdr->bLength - sizeof(*hwa_dn)); - break; - } - case WA_NOTIF_TRANSFER: - wa_handle_notif_xfer(wa, notif_hdr); - break; - case HWA_NOTIF_BPST_ADJ: - break; /* no action needed for BPST ADJ. */ - case DWA_NOTIF_RWAKE: - case DWA_NOTIF_PORTSTATUS: - /* FIXME: unimplemented WA NOTIFs */ - /* fallthru */ - default: - dev_err(dev, "HWA: unknown notification 0x%x, " - "%zu bytes; discarding\n", - notif_hdr->bNotifyType, - (size_t)notif_hdr->bLength); - break; - } - } -out: - wa_put(wa); - kfree(nw); - return; - - /* THIS SHOULD NOT HAPPEN - * - * Buffer exahusted with partial data remaining; just warn and - * discard the data, as this should not happen. - */ -exhausted_buffer: - dev_warn(dev, "HWA: device sent short notification, " - "%d bytes missing; discarding %d bytes.\n", - missing, (int)size); - goto out; -} - -/* - * Deliver incoming WA notifications to the wusbwa workqueue - * - * @wa: Pointer the Wire Adapter Controller Data Streaming - * instance (part of an 'struct usb_hcd'). - * @size: Size of the received buffer - * @returns 0 if ok, < 0 errno code on error. - * - * The input buffer is @wa->nep_buffer, with @size bytes - * (guaranteed to fit in the allocated space, - * @wa->nep_buffer_size). - */ -static int wa_nep_queue(struct wahc *wa, size_t size) -{ - int result = 0; - struct device *dev = &wa->usb_iface->dev; - struct wa_notif_work *nw; - - /* dev_fnstart(dev, "(wa %p, size %zu)\n", wa, size); */ - BUG_ON(size > wa->nep_buffer_size); - if (size == 0) - goto out; - if (atomic_read(&wa->notifs_queued) > 200) { - if (printk_ratelimit()) - dev_err(dev, "Too many notifications queued, " - "throttling back\n"); - goto out; - } - nw = kzalloc(sizeof(*nw) + size, GFP_ATOMIC); - if (nw == NULL) { - if (printk_ratelimit()) - dev_err(dev, "No memory to queue notification\n"); - result = -ENOMEM; - goto out; - } - INIT_WORK(&nw->work, wa_notif_dispatch); - nw->wa = wa_get(wa); - nw->size = size; - memcpy(nw->data, wa->nep_buffer, size); - atomic_inc(&wa->notifs_queued); /* Throttling ctl */ - queue_work(wusbd, &nw->work); -out: - /* dev_fnend(dev, "(wa %p, size %zu) = result\n", wa, size, result); */ - return result; -} - -/* - * Callback for the notification event endpoint - * - * Check's that everything is fine and then passes the data to be - * queued to the workqueue. - */ -static void wa_nep_cb(struct urb *urb) -{ - int result; - struct wahc *wa = urb->context; - struct device *dev = &wa->usb_iface->dev; - - switch (result = urb->status) { - case 0: - result = wa_nep_queue(wa, urb->actual_length); - if (result < 0) - dev_err(dev, "NEP: unable to process notification(s): " - "%d\n", result); - break; - case -ECONNRESET: /* Not an error, but a controlled situation; */ - case -ENOENT: /* (we killed the URB)...so, no broadcast */ - case -ESHUTDOWN: - dev_dbg(dev, "NEP: going down %d\n", urb->status); - goto out; - default: /* On general errors, we retry unless it gets ugly */ - if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)) { - dev_err(dev, "NEP: URB max acceptable errors " - "exceeded, resetting device\n"); - wa_reset_all(wa); - goto out; - } - dev_err(dev, "NEP: URB error %d\n", urb->status); - } - result = wa_nep_arm(wa, GFP_ATOMIC); - if (result < 0) { - dev_err(dev, "NEP: cannot submit URB: %d\n", result); - wa_reset_all(wa); - } -out: - return; -} - -/* - * Initialize @wa's notification and event's endpoint stuff - * - * This includes the allocating the read buffer, the context ID - * allocation bitmap, the URB and submitting the URB. - */ -int wa_nep_create(struct wahc *wa, struct usb_interface *iface) -{ - int result; - struct usb_endpoint_descriptor *epd; - struct usb_device *usb_dev = interface_to_usbdev(iface); - struct device *dev = &iface->dev; - - edc_init(&wa->nep_edc); - epd = &iface->cur_altsetting->endpoint[0].desc; - wa->nep_buffer_size = 1024; - wa->nep_buffer = kmalloc(wa->nep_buffer_size, GFP_KERNEL); - if (!wa->nep_buffer) - goto error_nep_buffer; - wa->nep_urb = usb_alloc_urb(0, GFP_KERNEL); - if (wa->nep_urb == NULL) - goto error_urb_alloc; - usb_fill_int_urb(wa->nep_urb, usb_dev, - usb_rcvintpipe(usb_dev, epd->bEndpointAddress), - wa->nep_buffer, wa->nep_buffer_size, - wa_nep_cb, wa, epd->bInterval); - result = wa_nep_arm(wa, GFP_KERNEL); - if (result < 0) { - dev_err(dev, "Cannot submit notification URB: %d\n", result); - goto error_nep_arm; - } - return 0; - -error_nep_arm: - usb_free_urb(wa->nep_urb); -error_urb_alloc: - kfree(wa->nep_buffer); -error_nep_buffer: - return -ENOMEM; -} - -void wa_nep_destroy(struct wahc *wa) -{ - wa_nep_disarm(wa); - usb_free_urb(wa->nep_urb); - kfree(wa->nep_buffer); -} diff --git a/drivers/staging/wusbcore/wa-rpipe.c b/drivers/staging/wusbcore/wa-rpipe.c deleted file mode 100644 index a5734cbcd5ad..000000000000 --- a/drivers/staging/wusbcore/wa-rpipe.c +++ /dev/null @@ -1,539 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * WUSB Wire Adapter - * rpipe management - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * FIXME: docs - * - * RPIPE - * - * Targeted at different downstream endpoints - * - * Descriptor: use to config the remote pipe. - * - * The number of blocks could be dynamic (wBlocks in descriptor is - * 0)--need to schedule them then. - * - * Each bit in wa->rpipe_bm represents if an rpipe is being used or - * not. Rpipes are represented with a 'struct wa_rpipe' that is - * attached to the hcpriv member of a 'struct usb_host_endpoint'. - * - * When you need to xfer data to an endpoint, you get an rpipe for it - * with wa_ep_rpipe_get(), which gives you a reference to the rpipe - * and keeps a single one (the first one) with the endpoint. When you - * are done transferring, you drop that reference. At the end the - * rpipe is always allocated and bound to the endpoint. There it might - * be recycled when not used. - * - * Addresses: - * - * We use a 1:1 mapping mechanism between port address (0 based - * index, actually) and the address. The USB stack knows about this. - * - * USB Stack port number 4 (1 based) - * WUSB code port index 3 (0 based) - * USB Address 5 (2 based -- 0 is for default, 1 for root hub) - * - * Now, because we don't use the concept as default address exactly - * like the (wired) USB code does, we need to kind of skip it. So we - * never take addresses from the urb->pipe, but from the - * urb->dev->devnum, to make sure that we always have the right - * destination address. - */ -#include -#include -#include -#include - -#include "wusbhc.h" -#include "wa-hc.h" - -static int __rpipe_get_descr(struct wahc *wa, - struct usb_rpipe_descriptor *descr, u16 index) -{ - ssize_t result; - struct device *dev = &wa->usb_iface->dev; - - /* Get the RPIPE descriptor -- we cannot use the usb_get_descriptor() - * function because the arguments are different. - */ - result = usb_control_msg( - wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), - USB_REQ_GET_DESCRIPTOR, - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_RPIPE, - USB_DT_RPIPE<<8, index, descr, sizeof(*descr), - USB_CTRL_GET_TIMEOUT); - if (result < 0) { - dev_err(dev, "rpipe %u: get descriptor failed: %d\n", - index, (int)result); - goto error; - } - if (result < sizeof(*descr)) { - dev_err(dev, "rpipe %u: got short descriptor " - "(%zd vs %zd bytes needed)\n", - index, result, sizeof(*descr)); - result = -EINVAL; - goto error; - } - result = 0; - -error: - return result; -} - -/* - * - * The descriptor is assumed to be properly initialized (ie: you got - * it through __rpipe_get_descr()). - */ -static int __rpipe_set_descr(struct wahc *wa, - struct usb_rpipe_descriptor *descr, u16 index) -{ - ssize_t result; - struct device *dev = &wa->usb_iface->dev; - - /* we cannot use the usb_get_descriptor() function because the - * arguments are different. - */ - result = usb_control_msg( - wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - USB_REQ_SET_DESCRIPTOR, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, - USB_DT_RPIPE<<8, index, descr, sizeof(*descr), - USB_CTRL_SET_TIMEOUT); - if (result < 0) { - dev_err(dev, "rpipe %u: set descriptor failed: %d\n", - index, (int)result); - goto error; - } - if (result < sizeof(*descr)) { - dev_err(dev, "rpipe %u: sent short descriptor " - "(%zd vs %zd bytes required)\n", - index, result, sizeof(*descr)); - result = -EINVAL; - goto error; - } - result = 0; - -error: - return result; - -} - -static void rpipe_init(struct wa_rpipe *rpipe) -{ - kref_init(&rpipe->refcnt); - spin_lock_init(&rpipe->seg_lock); - INIT_LIST_HEAD(&rpipe->seg_list); - INIT_LIST_HEAD(&rpipe->list_node); -} - -static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx) -{ - unsigned long flags; - - spin_lock_irqsave(&wa->rpipe_lock, flags); - rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx); - if (rpipe_idx < wa->rpipes) - set_bit(rpipe_idx, wa->rpipe_bm); - spin_unlock_irqrestore(&wa->rpipe_lock, flags); - - return rpipe_idx; -} - -static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx) -{ - unsigned long flags; - - spin_lock_irqsave(&wa->rpipe_lock, flags); - clear_bit(rpipe_idx, wa->rpipe_bm); - spin_unlock_irqrestore(&wa->rpipe_lock, flags); -} - -void rpipe_destroy(struct kref *_rpipe) -{ - struct wa_rpipe *rpipe = container_of(_rpipe, struct wa_rpipe, refcnt); - u8 index = le16_to_cpu(rpipe->descr.wRPipeIndex); - - if (rpipe->ep) - rpipe->ep->hcpriv = NULL; - rpipe_put_idx(rpipe->wa, index); - wa_put(rpipe->wa); - kfree(rpipe); -} -EXPORT_SYMBOL_GPL(rpipe_destroy); - -/* - * Locate an idle rpipe, create an structure for it and return it - * - * @wa is referenced and unlocked - * @crs enum rpipe_attr, required endpoint characteristics - * - * The rpipe can be used only sequentially (not in parallel). - * - * The rpipe is moved into the "ready" state. - */ -static int rpipe_get_idle(struct wa_rpipe **prpipe, struct wahc *wa, u8 crs, - gfp_t gfp) -{ - int result; - unsigned rpipe_idx; - struct wa_rpipe *rpipe; - struct device *dev = &wa->usb_iface->dev; - - rpipe = kzalloc(sizeof(*rpipe), gfp); - if (rpipe == NULL) - return -ENOMEM; - rpipe_init(rpipe); - - /* Look for an idle pipe */ - for (rpipe_idx = 0; rpipe_idx < wa->rpipes; rpipe_idx++) { - rpipe_idx = rpipe_get_idx(wa, rpipe_idx); - if (rpipe_idx >= wa->rpipes) /* no more pipes :( */ - break; - result = __rpipe_get_descr(wa, &rpipe->descr, rpipe_idx); - if (result < 0) - dev_err(dev, "Can't get descriptor for rpipe %u: %d\n", - rpipe_idx, result); - else if ((rpipe->descr.bmCharacteristics & crs) != 0) - goto found; - rpipe_put_idx(wa, rpipe_idx); - } - *prpipe = NULL; - kfree(rpipe); - return -ENXIO; - -found: - set_bit(rpipe_idx, wa->rpipe_bm); - rpipe->wa = wa_get(wa); - *prpipe = rpipe; - return 0; -} - -static int __rpipe_reset(struct wahc *wa, unsigned index) -{ - int result; - struct device *dev = &wa->usb_iface->dev; - - result = usb_control_msg( - wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - USB_REQ_RPIPE_RESET, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, - 0, index, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (result < 0) - dev_err(dev, "rpipe %u: reset failed: %d\n", - index, result); - return result; -} - -/* - * Fake companion descriptor for ep0 - * - * See WUSB1.0[7.4.4], most of this is zero for bulk/int/ctl - */ -static struct usb_wireless_ep_comp_descriptor epc0 = { - .bLength = sizeof(epc0), - .bDescriptorType = USB_DT_WIRELESS_ENDPOINT_COMP, - .bMaxBurst = 1, - .bMaxSequence = 2, -}; - -/* - * Look for EP companion descriptor - * - * Get there, look for Inara in the endpoint's extra descriptors - */ -static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find( - struct device *dev, struct usb_host_endpoint *ep) -{ - void *itr; - size_t itr_size; - struct usb_descriptor_header *hdr; - struct usb_wireless_ep_comp_descriptor *epcd; - - if (ep->desc.bEndpointAddress == 0) { - epcd = &epc0; - goto out; - } - itr = ep->extra; - itr_size = ep->extralen; - epcd = NULL; - while (itr_size > 0) { - if (itr_size < sizeof(*hdr)) { - dev_err(dev, "HW Bug? ep 0x%02x: extra descriptors " - "at offset %zu: only %zu bytes left\n", - ep->desc.bEndpointAddress, - itr - (void *) ep->extra, itr_size); - break; - } - hdr = itr; - if (hdr->bDescriptorType == USB_DT_WIRELESS_ENDPOINT_COMP) { - epcd = itr; - break; - } - if (hdr->bLength > itr_size) { - dev_err(dev, "HW Bug? ep 0x%02x: extra descriptor " - "at offset %zu (type 0x%02x) " - "length %d but only %zu bytes left\n", - ep->desc.bEndpointAddress, - itr - (void *) ep->extra, hdr->bDescriptorType, - hdr->bLength, itr_size); - break; - } - itr += hdr->bLength; - itr_size -= hdr->bLength; - } -out: - return epcd; -} - -/* - * Aim an rpipe to its device & endpoint destination - * - * Make sure we change the address to unauthenticated if the device - * is WUSB and it is not authenticated. - */ -static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa, - struct usb_host_endpoint *ep, struct urb *urb, gfp_t gfp) -{ - int result = -ENOMSG; /* better code for lack of companion? */ - struct device *dev = &wa->usb_iface->dev; - struct usb_device *usb_dev = urb->dev; - struct usb_wireless_ep_comp_descriptor *epcd; - u32 ack_window, epcd_max_sequence; - u8 unauth; - - epcd = rpipe_epc_find(dev, ep); - if (epcd == NULL) { - dev_err(dev, "ep 0x%02x: can't find companion descriptor\n", - ep->desc.bEndpointAddress); - goto error; - } - unauth = usb_dev->wusb && !usb_dev->authenticated ? 0x80 : 0; - __rpipe_reset(wa, le16_to_cpu(rpipe->descr.wRPipeIndex)); - atomic_set(&rpipe->segs_available, - le16_to_cpu(rpipe->descr.wRequests)); - /* FIXME: block allocation system; request with queuing and timeout */ - /* FIXME: compute so seg_size > ep->maxpktsize */ - rpipe->descr.wBlocks = cpu_to_le16(16); /* given */ - /* ep0 maxpktsize is 0x200 (WUSB1.0[4.8.1]) */ - if (usb_endpoint_xfer_isoc(&ep->desc)) - rpipe->descr.wMaxPacketSize = epcd->wOverTheAirPacketSize; - else - rpipe->descr.wMaxPacketSize = ep->desc.wMaxPacketSize; - - rpipe->descr.hwa_bMaxBurst = max(min_t(unsigned int, - epcd->bMaxBurst, 16U), 1U); - rpipe->descr.hwa_bDeviceInfoIndex = - wusb_port_no_to_idx(urb->dev->portnum); - /* FIXME: use maximum speed as supported or recommended by device */ - rpipe->descr.bSpeed = usb_pipeendpoint(urb->pipe) == 0 ? - UWB_PHY_RATE_53 : UWB_PHY_RATE_200; - - dev_dbg(dev, "addr %u (0x%02x) rpipe #%u ep# %u speed %d\n", - urb->dev->devnum, urb->dev->devnum | unauth, - le16_to_cpu(rpipe->descr.wRPipeIndex), - usb_pipeendpoint(urb->pipe), rpipe->descr.bSpeed); - - rpipe->descr.hwa_reserved = 0; - - rpipe->descr.bEndpointAddress = ep->desc.bEndpointAddress; - /* FIXME: bDataSequence */ - rpipe->descr.bDataSequence = 0; - - /* start with base window of hwa_bMaxBurst bits starting at 0. */ - ack_window = 0xFFFFFFFF >> (32 - rpipe->descr.hwa_bMaxBurst); - rpipe->descr.dwCurrentWindow = cpu_to_le32(ack_window); - epcd_max_sequence = max(min_t(unsigned int, - epcd->bMaxSequence, 32U), 2U); - rpipe->descr.bMaxDataSequence = epcd_max_sequence - 1; - rpipe->descr.bInterval = ep->desc.bInterval; - if (usb_endpoint_xfer_isoc(&ep->desc)) - rpipe->descr.bOverTheAirInterval = epcd->bOverTheAirInterval; - else - rpipe->descr.bOverTheAirInterval = 0; /* 0 if not isoc */ - /* FIXME: xmit power & preamble blah blah */ - rpipe->descr.bmAttribute = (ep->desc.bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK); - /* rpipe->descr.bmCharacteristics RO */ - rpipe->descr.bmRetryOptions = (wa->wusb->retry_count & 0xF); - /* FIXME: use for assessing link quality? */ - rpipe->descr.wNumTransactionErrors = 0; - result = __rpipe_set_descr(wa, &rpipe->descr, - le16_to_cpu(rpipe->descr.wRPipeIndex)); - if (result < 0) { - dev_err(dev, "Cannot aim rpipe: %d\n", result); - goto error; - } - result = 0; -error: - return result; -} - -/* - * Check an aimed rpipe to make sure it points to where we want - * - * We use bit 19 of the Linux USB pipe bitmap for unauth vs auth - * space; when it is like that, we or 0x80 to make an unauth address. - */ -static int rpipe_check_aim(const struct wa_rpipe *rpipe, const struct wahc *wa, - const struct usb_host_endpoint *ep, - const struct urb *urb, gfp_t gfp) -{ - int result = 0; - struct device *dev = &wa->usb_iface->dev; - u8 portnum = wusb_port_no_to_idx(urb->dev->portnum); - -#define AIM_CHECK(rdf, val, text) \ - do { \ - if (rpipe->descr.rdf != (val)) { \ - dev_err(dev, \ - "rpipe aim discrepancy: " #rdf " " text "\n", \ - rpipe->descr.rdf, (val)); \ - result = -EINVAL; \ - WARN_ON(1); \ - } \ - } while (0) - AIM_CHECK(hwa_bDeviceInfoIndex, portnum, "(%u vs %u)"); - AIM_CHECK(bSpeed, usb_pipeendpoint(urb->pipe) == 0 ? - UWB_PHY_RATE_53 : UWB_PHY_RATE_200, - "(%u vs %u)"); - AIM_CHECK(bEndpointAddress, ep->desc.bEndpointAddress, "(%u vs %u)"); - AIM_CHECK(bInterval, ep->desc.bInterval, "(%u vs %u)"); - AIM_CHECK(bmAttribute, ep->desc.bmAttributes & 0x03, "(%u vs %u)"); -#undef AIM_CHECK - return result; -} - -#ifndef CONFIG_BUG -#define CONFIG_BUG 0 -#endif - -/* - * Make sure there is an rpipe allocated for an endpoint - * - * If already allocated, we just refcount it; if not, we get an - * idle one, aim it to the right location and take it. - * - * Attaches to ep->hcpriv and rpipe->ep to ep. - */ -int rpipe_get_by_ep(struct wahc *wa, struct usb_host_endpoint *ep, - struct urb *urb, gfp_t gfp) -{ - int result = 0; - struct device *dev = &wa->usb_iface->dev; - struct wa_rpipe *rpipe; - u8 eptype; - - mutex_lock(&wa->rpipe_mutex); - rpipe = ep->hcpriv; - if (rpipe != NULL) { - if (CONFIG_BUG == 1) { - result = rpipe_check_aim(rpipe, wa, ep, urb, gfp); - if (result < 0) - goto error; - } - __rpipe_get(rpipe); - dev_dbg(dev, "ep 0x%02x: reusing rpipe %u\n", - ep->desc.bEndpointAddress, - le16_to_cpu(rpipe->descr.wRPipeIndex)); - } else { - /* hmm, assign idle rpipe, aim it */ - result = -ENOBUFS; - eptype = ep->desc.bmAttributes & 0x03; - result = rpipe_get_idle(&rpipe, wa, 1 << eptype, gfp); - if (result < 0) - goto error; - result = rpipe_aim(rpipe, wa, ep, urb, gfp); - if (result < 0) { - rpipe_put(rpipe); - goto error; - } - ep->hcpriv = rpipe; - rpipe->ep = ep; - __rpipe_get(rpipe); /* for caching into ep->hcpriv */ - dev_dbg(dev, "ep 0x%02x: using rpipe %u\n", - ep->desc.bEndpointAddress, - le16_to_cpu(rpipe->descr.wRPipeIndex)); - } -error: - mutex_unlock(&wa->rpipe_mutex); - return result; -} - -/* - * Allocate the bitmap for each rpipe. - */ -int wa_rpipes_create(struct wahc *wa) -{ - wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes); - wa->rpipe_bm = bitmap_zalloc(wa->rpipes, GFP_KERNEL); - if (wa->rpipe_bm == NULL) - return -ENOMEM; - return 0; -} - -void wa_rpipes_destroy(struct wahc *wa) -{ - struct device *dev = &wa->usb_iface->dev; - - if (!bitmap_empty(wa->rpipe_bm, wa->rpipes)) { - WARN_ON(1); - dev_err(dev, "BUG: pipes not released on exit: %*pb\n", - wa->rpipes, wa->rpipe_bm); - } - bitmap_free(wa->rpipe_bm); -} - -/* - * Release resources allocated for an endpoint - * - * If there is an associated rpipe to this endpoint, Abort any pending - * transfers and put it. If the rpipe ends up being destroyed, - * __rpipe_destroy() will cleanup ep->hcpriv. - * - * This is called before calling hcd->stop(), so you don't need to do - * anything else in there. - */ -void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep) -{ - struct wa_rpipe *rpipe; - - mutex_lock(&wa->rpipe_mutex); - rpipe = ep->hcpriv; - if (rpipe != NULL) { - u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex); - - usb_control_msg( - wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - USB_REQ_RPIPE_ABORT, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, - 0, index, NULL, 0, USB_CTRL_SET_TIMEOUT); - rpipe_put(rpipe); - } - mutex_unlock(&wa->rpipe_mutex); -} -EXPORT_SYMBOL_GPL(rpipe_ep_disable); - -/* Clear the stalled status of an RPIPE. */ -void rpipe_clear_feature_stalled(struct wahc *wa, struct usb_host_endpoint *ep) -{ - struct wa_rpipe *rpipe; - - mutex_lock(&wa->rpipe_mutex); - rpipe = ep->hcpriv; - if (rpipe != NULL) { - u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex); - - usb_control_msg( - wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, - RPIPE_STALL, index, NULL, 0, USB_CTRL_SET_TIMEOUT); - } - mutex_unlock(&wa->rpipe_mutex); -} -EXPORT_SYMBOL_GPL(rpipe_clear_feature_stalled); diff --git a/drivers/staging/wusbcore/wa-xfer.c b/drivers/staging/wusbcore/wa-xfer.c deleted file mode 100644 index abf88cea37bb..000000000000 --- a/drivers/staging/wusbcore/wa-xfer.c +++ /dev/null @@ -1,2927 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * WUSB Wire Adapter - * Data transfer and URB enqueing - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * How transfers work: get a buffer, break it up in segments (segment - * size is a multiple of the maxpacket size). For each segment issue a - * segment request (struct wa_xfer_*), then send the data buffer if - * out or nothing if in (all over the DTO endpoint). - * - * For each submitted segment request, a notification will come over - * the NEP endpoint and a transfer result (struct xfer_result) will - * arrive in the DTI URB. Read it, get the xfer ID, see if there is - * data coming (inbound transfer), schedule a read and handle it. - * - * Sounds simple, it is a pain to implement. - * - * - * ENTRY POINTS - * - * FIXME - * - * LIFE CYCLE / STATE DIAGRAM - * - * FIXME - * - * THIS CODE IS DISGUSTING - * - * Warned you are; it's my second try and still not happy with it. - * - * NOTES: - * - * - No iso - * - * - Supports DMA xfers, control, bulk and maybe interrupt - * - * - Does not recycle unused rpipes - * - * An rpipe is assigned to an endpoint the first time it is used, - * and then it's there, assigned, until the endpoint is disabled - * (destroyed [{h,d}wahc_op_ep_disable()]. The assignment of the - * rpipe to the endpoint is done under the wa->rpipe_sem semaphore - * (should be a mutex). - * - * Two methods it could be done: - * - * (a) set up a timer every time an rpipe's use count drops to 1 - * (which means unused) or when a transfer ends. Reset the - * timer when a xfer is queued. If the timer expires, release - * the rpipe [see rpipe_ep_disable()]. - * - * (b) when looking for free rpipes to attach [rpipe_get_by_ep()], - * when none are found go over the list, check their endpoint - * and their activity record (if no last-xfer-done-ts in the - * last x seconds) take it - * - * However, due to the fact that we have a set of limited - * resources (max-segments-at-the-same-time per xfer, - * xfers-per-ripe, blocks-per-rpipe, rpipes-per-host), at the end - * we are going to have to rebuild all this based on an scheduler, - * to where we have a list of transactions to do and based on the - * availability of the different required components (blocks, - * rpipes, segment slots, etc), we go scheduling them. Painful. - */ -#include -#include -#include -#include -#include -#include - -#include "wa-hc.h" -#include "wusbhc.h" - -enum { - /* [WUSB] section 8.3.3 allocates 7 bits for the segment index. */ - WA_SEGS_MAX = 128, -}; - -enum wa_seg_status { - WA_SEG_NOTREADY, - WA_SEG_READY, - WA_SEG_DELAYED, - WA_SEG_SUBMITTED, - WA_SEG_PENDING, - WA_SEG_DTI_PENDING, - WA_SEG_DONE, - WA_SEG_ERROR, - WA_SEG_ABORTED, -}; - -static void wa_xfer_delayed_run(struct wa_rpipe *); -static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting); - -/* - * Life cycle governed by 'struct urb' (the refcount of the struct is - * that of the 'struct urb' and usb_free_urb() would free the whole - * struct). - */ -struct wa_seg { - struct urb tr_urb; /* transfer request urb. */ - struct urb *isoc_pack_desc_urb; /* for isoc packet descriptor. */ - struct urb *dto_urb; /* for data output. */ - struct list_head list_node; /* for rpipe->req_list */ - struct wa_xfer *xfer; /* out xfer */ - u8 index; /* which segment we are */ - int isoc_frame_count; /* number of isoc frames in this segment. */ - int isoc_frame_offset; /* starting frame offset in the xfer URB. */ - /* Isoc frame that the current transfer buffer corresponds to. */ - int isoc_frame_index; - int isoc_size; /* size of all isoc frames sent by this seg. */ - enum wa_seg_status status; - ssize_t result; /* bytes xfered or error */ - struct wa_xfer_hdr xfer_hdr; -}; - -static inline void wa_seg_init(struct wa_seg *seg) -{ - usb_init_urb(&seg->tr_urb); - - /* set the remaining memory to 0. */ - memset(((void *)seg) + sizeof(seg->tr_urb), 0, - sizeof(*seg) - sizeof(seg->tr_urb)); -} - -/* - * Protected by xfer->lock - * - */ -struct wa_xfer { - struct kref refcnt; - struct list_head list_node; - spinlock_t lock; - u32 id; - - struct wahc *wa; /* Wire adapter we are plugged to */ - struct usb_host_endpoint *ep; - struct urb *urb; /* URB we are transferring for */ - struct wa_seg **seg; /* transfer segments */ - u8 segs, segs_submitted, segs_done; - unsigned is_inbound:1; - unsigned is_dma:1; - size_t seg_size; - int result; - - gfp_t gfp; /* allocation mask */ - - struct wusb_dev *wusb_dev; /* for activity timestamps */ -}; - -static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer, - struct wa_seg *seg, int curr_iso_frame); -static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, - int starting_index, enum wa_seg_status status); - -static inline void wa_xfer_init(struct wa_xfer *xfer) -{ - kref_init(&xfer->refcnt); - INIT_LIST_HEAD(&xfer->list_node); - spin_lock_init(&xfer->lock); -} - -/* - * Destroy a transfer structure - * - * Note that freeing xfer->seg[cnt]->tr_urb will free the containing - * xfer->seg[cnt] memory that was allocated by __wa_xfer_setup_segs. - */ -static void wa_xfer_destroy(struct kref *_xfer) -{ - struct wa_xfer *xfer = container_of(_xfer, struct wa_xfer, refcnt); - if (xfer->seg) { - unsigned cnt; - for (cnt = 0; cnt < xfer->segs; cnt++) { - struct wa_seg *seg = xfer->seg[cnt]; - if (seg) { - usb_free_urb(seg->isoc_pack_desc_urb); - if (seg->dto_urb) { - kfree(seg->dto_urb->sg); - usb_free_urb(seg->dto_urb); - } - usb_free_urb(&seg->tr_urb); - } - } - kfree(xfer->seg); - } - kfree(xfer); -} - -static void wa_xfer_get(struct wa_xfer *xfer) -{ - kref_get(&xfer->refcnt); -} - -static void wa_xfer_put(struct wa_xfer *xfer) -{ - kref_put(&xfer->refcnt, wa_xfer_destroy); -} - -/* - * Try to get exclusive access to the DTO endpoint resource. Return true - * if successful. - */ -static inline int __wa_dto_try_get(struct wahc *wa) -{ - return (test_and_set_bit(0, &wa->dto_in_use) == 0); -} - -/* Release the DTO endpoint resource. */ -static inline void __wa_dto_put(struct wahc *wa) -{ - clear_bit_unlock(0, &wa->dto_in_use); -} - -/* Service RPIPEs that are waiting on the DTO resource. */ -static void wa_check_for_delayed_rpipes(struct wahc *wa) -{ - unsigned long flags; - int dto_waiting = 0; - struct wa_rpipe *rpipe; - - spin_lock_irqsave(&wa->rpipe_lock, flags); - while (!list_empty(&wa->rpipe_delayed_list) && !dto_waiting) { - rpipe = list_first_entry(&wa->rpipe_delayed_list, - struct wa_rpipe, list_node); - __wa_xfer_delayed_run(rpipe, &dto_waiting); - /* remove this RPIPE from the list if it is not waiting. */ - if (!dto_waiting) { - pr_debug("%s: RPIPE %d serviced and removed from delayed list.\n", - __func__, - le16_to_cpu(rpipe->descr.wRPipeIndex)); - list_del_init(&rpipe->list_node); - } - } - spin_unlock_irqrestore(&wa->rpipe_lock, flags); -} - -/* add this RPIPE to the end of the delayed RPIPE list. */ -static void wa_add_delayed_rpipe(struct wahc *wa, struct wa_rpipe *rpipe) -{ - unsigned long flags; - - spin_lock_irqsave(&wa->rpipe_lock, flags); - /* add rpipe to the list if it is not already on it. */ - if (list_empty(&rpipe->list_node)) { - pr_debug("%s: adding RPIPE %d to the delayed list.\n", - __func__, le16_to_cpu(rpipe->descr.wRPipeIndex)); - list_add_tail(&rpipe->list_node, &wa->rpipe_delayed_list); - } - spin_unlock_irqrestore(&wa->rpipe_lock, flags); -} - -/* - * xfer is referenced - * - * xfer->lock has to be unlocked - * - * We take xfer->lock for setting the result; this is a barrier - * against drivers/usb/core/hcd.c:unlink1() being called after we call - * usb_hcd_giveback_urb() and wa_urb_dequeue() trying to get a - * reference to the transfer. - */ -static void wa_xfer_giveback(struct wa_xfer *xfer) -{ - unsigned long flags; - - spin_lock_irqsave(&xfer->wa->xfer_list_lock, flags); - list_del_init(&xfer->list_node); - usb_hcd_unlink_urb_from_ep(&(xfer->wa->wusb->usb_hcd), xfer->urb); - spin_unlock_irqrestore(&xfer->wa->xfer_list_lock, flags); - /* FIXME: segmentation broken -- kills DWA */ - wusbhc_giveback_urb(xfer->wa->wusb, xfer->urb, xfer->result); - wa_put(xfer->wa); - wa_xfer_put(xfer); -} - -/* - * xfer is referenced - * - * xfer->lock has to be unlocked - */ -static void wa_xfer_completion(struct wa_xfer *xfer) -{ - if (xfer->wusb_dev) - wusb_dev_put(xfer->wusb_dev); - rpipe_put(xfer->ep->hcpriv); - wa_xfer_giveback(xfer); -} - -/* - * Initialize a transfer's ID - * - * We need to use a sequential number; if we use the pointer or the - * hash of the pointer, it can repeat over sequential transfers and - * then it will confuse the HWA....wonder why in hell they put a 32 - * bit handle in there then. - */ -static void wa_xfer_id_init(struct wa_xfer *xfer) -{ - xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count); -} - -/* Return the xfer's ID. */ -static inline u32 wa_xfer_id(struct wa_xfer *xfer) -{ - return xfer->id; -} - -/* Return the xfer's ID in transport format (little endian). */ -static inline __le32 wa_xfer_id_le32(struct wa_xfer *xfer) -{ - return cpu_to_le32(xfer->id); -} - -/* - * If transfer is done, wrap it up and return true - * - * xfer->lock has to be locked - */ -static unsigned __wa_xfer_is_done(struct wa_xfer *xfer) -{ - struct device *dev = &xfer->wa->usb_iface->dev; - unsigned result, cnt; - struct wa_seg *seg; - struct urb *urb = xfer->urb; - unsigned found_short = 0; - - result = xfer->segs_done == xfer->segs_submitted; - if (result == 0) - goto out; - urb->actual_length = 0; - for (cnt = 0; cnt < xfer->segs; cnt++) { - seg = xfer->seg[cnt]; - switch (seg->status) { - case WA_SEG_DONE: - if (found_short && seg->result > 0) { - dev_dbg(dev, "xfer %p ID %08X#%u: bad short segments (%zu)\n", - xfer, wa_xfer_id(xfer), cnt, - seg->result); - urb->status = -EINVAL; - goto out; - } - urb->actual_length += seg->result; - if (!(usb_pipeisoc(xfer->urb->pipe)) - && seg->result < xfer->seg_size - && cnt != xfer->segs-1) - found_short = 1; - dev_dbg(dev, "xfer %p ID %08X#%u: DONE short %d " - "result %zu urb->actual_length %d\n", - xfer, wa_xfer_id(xfer), seg->index, found_short, - seg->result, urb->actual_length); - break; - case WA_SEG_ERROR: - xfer->result = seg->result; - dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zi(0x%08zX)\n", - xfer, wa_xfer_id(xfer), seg->index, seg->result, - seg->result); - goto out; - case WA_SEG_ABORTED: - xfer->result = seg->result; - dev_dbg(dev, "xfer %p ID %08X#%u: ABORTED result %zi(0x%08zX)\n", - xfer, wa_xfer_id(xfer), seg->index, seg->result, - seg->result); - goto out; - default: - dev_warn(dev, "xfer %p ID %08X#%u: is_done bad state %d\n", - xfer, wa_xfer_id(xfer), cnt, seg->status); - xfer->result = -EINVAL; - goto out; - } - } - xfer->result = 0; -out: - return result; -} - -/* - * Mark the given segment as done. Return true if this completes the xfer. - * This should only be called for segs that have been submitted to an RPIPE. - * Delayed segs are not marked as submitted so they do not need to be marked - * as done when cleaning up. - * - * xfer->lock has to be locked - */ -static unsigned __wa_xfer_mark_seg_as_done(struct wa_xfer *xfer, - struct wa_seg *seg, enum wa_seg_status status) -{ - seg->status = status; - xfer->segs_done++; - - /* check for done. */ - return __wa_xfer_is_done(xfer); -} - -/* - * Search for a transfer list ID on the HCD's URB list - * - * For 32 bit architectures, we use the pointer itself; for 64 bits, a - * 32-bit hash of the pointer. - * - * @returns NULL if not found. - */ -static struct wa_xfer *wa_xfer_get_by_id(struct wahc *wa, u32 id) -{ - unsigned long flags; - struct wa_xfer *xfer_itr; - spin_lock_irqsave(&wa->xfer_list_lock, flags); - list_for_each_entry(xfer_itr, &wa->xfer_list, list_node) { - if (id == xfer_itr->id) { - wa_xfer_get(xfer_itr); - goto out; - } - } - xfer_itr = NULL; -out: - spin_unlock_irqrestore(&wa->xfer_list_lock, flags); - return xfer_itr; -} - -struct wa_xfer_abort_buffer { - struct urb urb; - struct wahc *wa; - struct wa_xfer_abort cmd; -}; - -static void __wa_xfer_abort_cb(struct urb *urb) -{ - struct wa_xfer_abort_buffer *b = urb->context; - struct wahc *wa = b->wa; - - /* - * If the abort request URB failed, then the HWA did not get the abort - * command. Forcibly clean up the xfer without waiting for a Transfer - * Result from the HWA. - */ - if (urb->status < 0) { - struct wa_xfer *xfer; - struct device *dev = &wa->usb_iface->dev; - - xfer = wa_xfer_get_by_id(wa, le32_to_cpu(b->cmd.dwTransferID)); - dev_err(dev, "%s: Transfer Abort request failed. result: %d\n", - __func__, urb->status); - if (xfer) { - unsigned long flags; - int done, seg_index = 0; - struct wa_rpipe *rpipe = xfer->ep->hcpriv; - - dev_err(dev, "%s: cleaning up xfer %p ID 0x%08X.\n", - __func__, xfer, wa_xfer_id(xfer)); - spin_lock_irqsave(&xfer->lock, flags); - /* skip done segs. */ - while (seg_index < xfer->segs) { - struct wa_seg *seg = xfer->seg[seg_index]; - - if ((seg->status == WA_SEG_DONE) || - (seg->status == WA_SEG_ERROR)) { - ++seg_index; - } else { - break; - } - } - /* mark remaining segs as aborted. */ - wa_complete_remaining_xfer_segs(xfer, seg_index, - WA_SEG_ABORTED); - done = __wa_xfer_is_done(xfer); - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - wa_xfer_delayed_run(rpipe); - wa_xfer_put(xfer); - } else { - dev_err(dev, "%s: xfer ID 0x%08X already gone.\n", - __func__, le32_to_cpu(b->cmd.dwTransferID)); - } - } - - wa_put(wa); /* taken in __wa_xfer_abort */ - usb_put_urb(&b->urb); -} - -/* - * Aborts an ongoing transaction - * - * Assumes the transfer is referenced and locked and in a submitted - * state (mainly that there is an endpoint/rpipe assigned). - * - * The callback (see above) does nothing but freeing up the data by - * putting the URB. Because the URB is allocated at the head of the - * struct, the whole space we allocated is kfreed. * - */ -static int __wa_xfer_abort(struct wa_xfer *xfer) -{ - int result = -ENOMEM; - struct device *dev = &xfer->wa->usb_iface->dev; - struct wa_xfer_abort_buffer *b; - struct wa_rpipe *rpipe = xfer->ep->hcpriv; - - b = kmalloc(sizeof(*b), GFP_ATOMIC); - if (b == NULL) - goto error_kmalloc; - b->cmd.bLength = sizeof(b->cmd); - b->cmd.bRequestType = WA_XFER_ABORT; - b->cmd.wRPipe = rpipe->descr.wRPipeIndex; - b->cmd.dwTransferID = wa_xfer_id_le32(xfer); - b->wa = wa_get(xfer->wa); - - usb_init_urb(&b->urb); - usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev, - usb_sndbulkpipe(xfer->wa->usb_dev, - xfer->wa->dto_epd->bEndpointAddress), - &b->cmd, sizeof(b->cmd), __wa_xfer_abort_cb, b); - result = usb_submit_urb(&b->urb, GFP_ATOMIC); - if (result < 0) - goto error_submit; - return result; /* callback frees! */ - - -error_submit: - wa_put(xfer->wa); - if (printk_ratelimit()) - dev_err(dev, "xfer %p: Can't submit abort request: %d\n", - xfer, result); - kfree(b); -error_kmalloc: - return result; - -} - -/* - * Calculate the number of isoc frames starting from isoc_frame_offset - * that will fit a in transfer segment. - */ -static int __wa_seg_calculate_isoc_frame_count(struct wa_xfer *xfer, - int isoc_frame_offset, int *total_size) -{ - int segment_size = 0, frame_count = 0; - int index = isoc_frame_offset; - struct usb_iso_packet_descriptor *iso_frame_desc = - xfer->urb->iso_frame_desc; - - while ((index < xfer->urb->number_of_packets) - && ((segment_size + iso_frame_desc[index].length) - <= xfer->seg_size)) { - /* - * For Alereon HWA devices, only include an isoc frame in an - * out segment if it is physically contiguous with the previous - * frame. This is required because those devices expect - * the isoc frames to be sent as a single USB transaction as - * opposed to one transaction per frame with standard HWA. - */ - if ((xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) - && (xfer->is_inbound == 0) - && (index > isoc_frame_offset) - && ((iso_frame_desc[index - 1].offset + - iso_frame_desc[index - 1].length) != - iso_frame_desc[index].offset)) - break; - - /* this frame fits. count it. */ - ++frame_count; - segment_size += iso_frame_desc[index].length; - - /* move to the next isoc frame. */ - ++index; - } - - *total_size = segment_size; - return frame_count; -} - -/* - * - * @returns < 0 on error, transfer segment request size if ok - */ -static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer, - enum wa_xfer_type *pxfer_type) -{ - ssize_t result; - struct device *dev = &xfer->wa->usb_iface->dev; - size_t maxpktsize; - struct urb *urb = xfer->urb; - struct wa_rpipe *rpipe = xfer->ep->hcpriv; - - switch (rpipe->descr.bmAttribute & 0x3) { - case USB_ENDPOINT_XFER_CONTROL: - *pxfer_type = WA_XFER_TYPE_CTL; - result = sizeof(struct wa_xfer_ctl); - break; - case USB_ENDPOINT_XFER_INT: - case USB_ENDPOINT_XFER_BULK: - *pxfer_type = WA_XFER_TYPE_BI; - result = sizeof(struct wa_xfer_bi); - break; - case USB_ENDPOINT_XFER_ISOC: - *pxfer_type = WA_XFER_TYPE_ISO; - result = sizeof(struct wa_xfer_hwaiso); - break; - default: - /* never happens */ - BUG(); - result = -EINVAL; /* shut gcc up */ - } - xfer->is_inbound = urb->pipe & USB_DIR_IN ? 1 : 0; - xfer->is_dma = urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? 1 : 0; - - maxpktsize = le16_to_cpu(rpipe->descr.wMaxPacketSize); - xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks) - * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1); - /* Compute the segment size and make sure it is a multiple of - * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of - * a check (FIXME) */ - if (xfer->seg_size < maxpktsize) { - dev_err(dev, - "HW BUG? seg_size %zu smaller than maxpktsize %zu\n", - xfer->seg_size, maxpktsize); - result = -EINVAL; - goto error; - } - xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize; - if ((rpipe->descr.bmAttribute & 0x3) == USB_ENDPOINT_XFER_ISOC) { - int index = 0; - - xfer->segs = 0; - /* - * loop over urb->number_of_packets to determine how many - * xfer segments will be needed to send the isoc frames. - */ - while (index < urb->number_of_packets) { - int seg_size; /* don't care. */ - index += __wa_seg_calculate_isoc_frame_count(xfer, - index, &seg_size); - ++xfer->segs; - } - } else { - xfer->segs = DIV_ROUND_UP(urb->transfer_buffer_length, - xfer->seg_size); - if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL) - xfer->segs = 1; - } - - if (xfer->segs > WA_SEGS_MAX) { - dev_err(dev, "BUG? oops, number of segments %zu bigger than %d\n", - (urb->transfer_buffer_length/xfer->seg_size), - WA_SEGS_MAX); - result = -EINVAL; - goto error; - } -error: - return result; -} - -static void __wa_setup_isoc_packet_descr( - struct wa_xfer_packet_info_hwaiso *packet_desc, - struct wa_xfer *xfer, - struct wa_seg *seg) { - struct usb_iso_packet_descriptor *iso_frame_desc = - xfer->urb->iso_frame_desc; - int frame_index; - - /* populate isoc packet descriptor. */ - packet_desc->bPacketType = WA_XFER_ISO_PACKET_INFO; - packet_desc->wLength = cpu_to_le16(struct_size(packet_desc, - PacketLength, - seg->isoc_frame_count)); - for (frame_index = 0; frame_index < seg->isoc_frame_count; - ++frame_index) { - int offset_index = frame_index + seg->isoc_frame_offset; - packet_desc->PacketLength[frame_index] = - cpu_to_le16(iso_frame_desc[offset_index].length); - } -} - - -/* Fill in the common request header and xfer-type specific data. */ -static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer, - struct wa_xfer_hdr *xfer_hdr0, - enum wa_xfer_type xfer_type, - size_t xfer_hdr_size) -{ - struct wa_rpipe *rpipe = xfer->ep->hcpriv; - struct wa_seg *seg = xfer->seg[0]; - - xfer_hdr0 = &seg->xfer_hdr; - xfer_hdr0->bLength = xfer_hdr_size; - xfer_hdr0->bRequestType = xfer_type; - xfer_hdr0->wRPipe = rpipe->descr.wRPipeIndex; - xfer_hdr0->dwTransferID = wa_xfer_id_le32(xfer); - xfer_hdr0->bTransferSegment = 0; - switch (xfer_type) { - case WA_XFER_TYPE_CTL: { - struct wa_xfer_ctl *xfer_ctl = - container_of(xfer_hdr0, struct wa_xfer_ctl, hdr); - xfer_ctl->bmAttribute = xfer->is_inbound ? 1 : 0; - memcpy(&xfer_ctl->baSetupData, xfer->urb->setup_packet, - sizeof(xfer_ctl->baSetupData)); - break; - } - case WA_XFER_TYPE_BI: - break; - case WA_XFER_TYPE_ISO: { - struct wa_xfer_hwaiso *xfer_iso = - container_of(xfer_hdr0, struct wa_xfer_hwaiso, hdr); - struct wa_xfer_packet_info_hwaiso *packet_desc = - ((void *)xfer_iso) + xfer_hdr_size; - - /* populate the isoc section of the transfer request. */ - xfer_iso->dwNumOfPackets = cpu_to_le32(seg->isoc_frame_count); - /* populate isoc packet descriptor. */ - __wa_setup_isoc_packet_descr(packet_desc, xfer, seg); - break; - } - default: - BUG(); - }; -} - -/* - * Callback for the OUT data phase of the segment request - * - * Check wa_seg_tr_cb(); most comments also apply here because this - * function does almost the same thing and they work closely - * together. - * - * If the seg request has failed but this DTO phase has succeeded, - * wa_seg_tr_cb() has already failed the segment and moved the - * status to WA_SEG_ERROR, so this will go through 'case 0' and - * effectively do nothing. - */ -static void wa_seg_dto_cb(struct urb *urb) -{ - struct wa_seg *seg = urb->context; - struct wa_xfer *xfer = seg->xfer; - struct wahc *wa; - struct device *dev; - struct wa_rpipe *rpipe; - unsigned long flags; - unsigned rpipe_ready = 0; - int data_send_done = 1, release_dto = 0, holding_dto = 0; - u8 done = 0; - int result; - - /* free the sg if it was used. */ - kfree(urb->sg); - urb->sg = NULL; - - spin_lock_irqsave(&xfer->lock, flags); - wa = xfer->wa; - dev = &wa->usb_iface->dev; - if (usb_pipeisoc(xfer->urb->pipe)) { - /* Alereon HWA sends all isoc frames in a single transfer. */ - if (wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) - seg->isoc_frame_index += seg->isoc_frame_count; - else - seg->isoc_frame_index += 1; - if (seg->isoc_frame_index < seg->isoc_frame_count) { - data_send_done = 0; - holding_dto = 1; /* checked in error cases. */ - /* - * if this is the last isoc frame of the segment, we - * can release DTO after sending this frame. - */ - if ((seg->isoc_frame_index + 1) >= - seg->isoc_frame_count) - release_dto = 1; - } - dev_dbg(dev, "xfer 0x%08X#%u: isoc frame = %d, holding_dto = %d, release_dto = %d.\n", - wa_xfer_id(xfer), seg->index, seg->isoc_frame_index, - holding_dto, release_dto); - } - spin_unlock_irqrestore(&xfer->lock, flags); - - switch (urb->status) { - case 0: - spin_lock_irqsave(&xfer->lock, flags); - seg->result += urb->actual_length; - if (data_send_done) { - dev_dbg(dev, "xfer 0x%08X#%u: data out done (%zu bytes)\n", - wa_xfer_id(xfer), seg->index, seg->result); - if (seg->status < WA_SEG_PENDING) - seg->status = WA_SEG_PENDING; - } else { - /* should only hit this for isoc xfers. */ - /* - * Populate the dto URB with the next isoc frame buffer, - * send the URB and release DTO if we no longer need it. - */ - __wa_populate_dto_urb_isoc(xfer, seg, - seg->isoc_frame_offset + seg->isoc_frame_index); - - /* resubmit the URB with the next isoc frame. */ - /* take a ref on resubmit. */ - wa_xfer_get(xfer); - result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); - if (result < 0) { - dev_err(dev, "xfer 0x%08X#%u: DTO submit failed: %d\n", - wa_xfer_id(xfer), seg->index, result); - spin_unlock_irqrestore(&xfer->lock, flags); - goto error_dto_submit; - } - } - spin_unlock_irqrestore(&xfer->lock, flags); - if (release_dto) { - __wa_dto_put(wa); - wa_check_for_delayed_rpipes(wa); - } - break; - case -ECONNRESET: /* URB unlinked; no need to do anything */ - case -ENOENT: /* as it was done by the who unlinked us */ - if (holding_dto) { - __wa_dto_put(wa); - wa_check_for_delayed_rpipes(wa); - } - break; - default: /* Other errors ... */ - dev_err(dev, "xfer 0x%08X#%u: data out error %d\n", - wa_xfer_id(xfer), seg->index, urb->status); - goto error_default; - } - - /* taken when this URB was submitted. */ - wa_xfer_put(xfer); - return; - -error_dto_submit: - /* taken on resubmit attempt. */ - wa_xfer_put(xfer); -error_default: - spin_lock_irqsave(&xfer->lock, flags); - rpipe = xfer->ep->hcpriv; - if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)){ - dev_err(dev, "DTO: URB max acceptable errors exceeded, resetting device\n"); - wa_reset_all(wa); - } - if (seg->status != WA_SEG_ERROR) { - seg->result = urb->status; - __wa_xfer_abort(xfer); - rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR); - } - spin_unlock_irqrestore(&xfer->lock, flags); - if (holding_dto) { - __wa_dto_put(wa); - wa_check_for_delayed_rpipes(wa); - } - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - /* taken when this URB was submitted. */ - wa_xfer_put(xfer); -} - -/* - * Callback for the isoc packet descriptor phase of the segment request - * - * Check wa_seg_tr_cb(); most comments also apply here because this - * function does almost the same thing and they work closely - * together. - * - * If the seg request has failed but this phase has succeeded, - * wa_seg_tr_cb() has already failed the segment and moved the - * status to WA_SEG_ERROR, so this will go through 'case 0' and - * effectively do nothing. - */ -static void wa_seg_iso_pack_desc_cb(struct urb *urb) -{ - struct wa_seg *seg = urb->context; - struct wa_xfer *xfer = seg->xfer; - struct wahc *wa; - struct device *dev; - struct wa_rpipe *rpipe; - unsigned long flags; - unsigned rpipe_ready = 0; - u8 done = 0; - - switch (urb->status) { - case 0: - spin_lock_irqsave(&xfer->lock, flags); - wa = xfer->wa; - dev = &wa->usb_iface->dev; - dev_dbg(dev, "iso xfer %08X#%u: packet descriptor done\n", - wa_xfer_id(xfer), seg->index); - if (xfer->is_inbound && seg->status < WA_SEG_PENDING) - seg->status = WA_SEG_PENDING; - spin_unlock_irqrestore(&xfer->lock, flags); - break; - case -ECONNRESET: /* URB unlinked; no need to do anything */ - case -ENOENT: /* as it was done by the who unlinked us */ - break; - default: /* Other errors ... */ - spin_lock_irqsave(&xfer->lock, flags); - wa = xfer->wa; - dev = &wa->usb_iface->dev; - rpipe = xfer->ep->hcpriv; - pr_err_ratelimited("iso xfer %08X#%u: packet descriptor error %d\n", - wa_xfer_id(xfer), seg->index, urb->status); - if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)){ - dev_err(dev, "iso xfer: URB max acceptable errors exceeded, resetting device\n"); - wa_reset_all(wa); - } - if (seg->status != WA_SEG_ERROR) { - usb_unlink_urb(seg->dto_urb); - seg->result = urb->status; - __wa_xfer_abort(xfer); - rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_mark_seg_as_done(xfer, seg, - WA_SEG_ERROR); - } - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - } - /* taken when this URB was submitted. */ - wa_xfer_put(xfer); -} - -/* - * Callback for the segment request - * - * If successful transition state (unless already transitioned or - * outbound transfer); otherwise, take a note of the error, mark this - * segment done and try completion. - * - * Note we don't access until we are sure that the transfer hasn't - * been cancelled (ECONNRESET, ENOENT), which could mean that - * seg->xfer could be already gone. - * - * We have to check before setting the status to WA_SEG_PENDING - * because sometimes the xfer result callback arrives before this - * callback (geeeeeeze), so it might happen that we are already in - * another state. As well, we don't set it if the transfer is not inbound, - * as in that case, wa_seg_dto_cb will do it when the OUT data phase - * finishes. - */ -static void wa_seg_tr_cb(struct urb *urb) -{ - struct wa_seg *seg = urb->context; - struct wa_xfer *xfer = seg->xfer; - struct wahc *wa; - struct device *dev; - struct wa_rpipe *rpipe; - unsigned long flags; - unsigned rpipe_ready; - u8 done = 0; - - switch (urb->status) { - case 0: - spin_lock_irqsave(&xfer->lock, flags); - wa = xfer->wa; - dev = &wa->usb_iface->dev; - dev_dbg(dev, "xfer %p ID 0x%08X#%u: request done\n", - xfer, wa_xfer_id(xfer), seg->index); - if (xfer->is_inbound && - seg->status < WA_SEG_PENDING && - !(usb_pipeisoc(xfer->urb->pipe))) - seg->status = WA_SEG_PENDING; - spin_unlock_irqrestore(&xfer->lock, flags); - break; - case -ECONNRESET: /* URB unlinked; no need to do anything */ - case -ENOENT: /* as it was done by the who unlinked us */ - break; - default: /* Other errors ... */ - spin_lock_irqsave(&xfer->lock, flags); - wa = xfer->wa; - dev = &wa->usb_iface->dev; - rpipe = xfer->ep->hcpriv; - if (printk_ratelimit()) - dev_err(dev, "xfer %p ID 0x%08X#%u: request error %d\n", - xfer, wa_xfer_id(xfer), seg->index, - urb->status); - if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)){ - dev_err(dev, "DTO: URB max acceptable errors " - "exceeded, resetting device\n"); - wa_reset_all(wa); - } - usb_unlink_urb(seg->isoc_pack_desc_urb); - usb_unlink_urb(seg->dto_urb); - seg->result = urb->status; - __wa_xfer_abort(xfer); - rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR); - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - } - /* taken when this URB was submitted. */ - wa_xfer_put(xfer); -} - -/* - * Allocate an SG list to store bytes_to_transfer bytes and copy the - * subset of the in_sg that matches the buffer subset - * we are about to transfer. - */ -static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg, - const unsigned int bytes_transferred, - const unsigned int bytes_to_transfer, int *out_num_sgs) -{ - struct scatterlist *out_sg; - unsigned int bytes_processed = 0, offset_into_current_page_data = 0, - nents; - struct scatterlist *current_xfer_sg = in_sg; - struct scatterlist *current_seg_sg, *last_seg_sg; - - /* skip previously transferred pages. */ - while ((current_xfer_sg) && - (bytes_processed < bytes_transferred)) { - bytes_processed += current_xfer_sg->length; - - /* advance the sg if current segment starts on or past the - next page. */ - if (bytes_processed <= bytes_transferred) - current_xfer_sg = sg_next(current_xfer_sg); - } - - /* the data for the current segment starts in current_xfer_sg. - calculate the offset. */ - if (bytes_processed > bytes_transferred) { - offset_into_current_page_data = current_xfer_sg->length - - (bytes_processed - bytes_transferred); - } - - /* calculate the number of pages needed by this segment. */ - nents = DIV_ROUND_UP((bytes_to_transfer + - offset_into_current_page_data + - current_xfer_sg->offset), - PAGE_SIZE); - - out_sg = kmalloc((sizeof(struct scatterlist) * nents), GFP_ATOMIC); - if (out_sg) { - sg_init_table(out_sg, nents); - - /* copy the portion of the incoming SG that correlates to the - * data to be transferred by this segment to the segment SG. */ - last_seg_sg = current_seg_sg = out_sg; - bytes_processed = 0; - - /* reset nents and calculate the actual number of sg entries - needed. */ - nents = 0; - while ((bytes_processed < bytes_to_transfer) && - current_seg_sg && current_xfer_sg) { - unsigned int page_len = min((current_xfer_sg->length - - offset_into_current_page_data), - (bytes_to_transfer - bytes_processed)); - - sg_set_page(current_seg_sg, sg_page(current_xfer_sg), - page_len, - current_xfer_sg->offset + - offset_into_current_page_data); - - bytes_processed += page_len; - - last_seg_sg = current_seg_sg; - current_seg_sg = sg_next(current_seg_sg); - current_xfer_sg = sg_next(current_xfer_sg); - - /* only the first page may require additional offset. */ - offset_into_current_page_data = 0; - nents++; - } - - /* update num_sgs and terminate the list since we may have - * concatenated pages. */ - sg_mark_end(last_seg_sg); - *out_num_sgs = nents; - } - - return out_sg; -} - -/* - * Populate DMA buffer info for the isoc dto urb. - */ -static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer, - struct wa_seg *seg, int curr_iso_frame) -{ - seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - seg->dto_urb->sg = NULL; - seg->dto_urb->num_sgs = 0; - /* dto urb buffer address pulled from iso_frame_desc. */ - seg->dto_urb->transfer_dma = xfer->urb->transfer_dma + - xfer->urb->iso_frame_desc[curr_iso_frame].offset; - /* The Alereon HWA sends a single URB with all isoc segs. */ - if (xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) - seg->dto_urb->transfer_buffer_length = seg->isoc_size; - else - seg->dto_urb->transfer_buffer_length = - xfer->urb->iso_frame_desc[curr_iso_frame].length; -} - -/* - * Populate buffer ptr and size, DMA buffer or SG list for the dto urb. - */ -static int __wa_populate_dto_urb(struct wa_xfer *xfer, - struct wa_seg *seg, size_t buf_itr_offset, size_t buf_itr_size) -{ - int result = 0; - - if (xfer->is_dma) { - seg->dto_urb->transfer_dma = - xfer->urb->transfer_dma + buf_itr_offset; - seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - seg->dto_urb->sg = NULL; - seg->dto_urb->num_sgs = 0; - } else { - /* do buffer or SG processing. */ - seg->dto_urb->transfer_flags &= - ~URB_NO_TRANSFER_DMA_MAP; - /* this should always be 0 before a resubmit. */ - seg->dto_urb->num_mapped_sgs = 0; - - if (xfer->urb->transfer_buffer) { - seg->dto_urb->transfer_buffer = - xfer->urb->transfer_buffer + - buf_itr_offset; - seg->dto_urb->sg = NULL; - seg->dto_urb->num_sgs = 0; - } else { - seg->dto_urb->transfer_buffer = NULL; - - /* - * allocate an SG list to store seg_size bytes - * and copy the subset of the xfer->urb->sg that - * matches the buffer subset we are about to - * read. - */ - seg->dto_urb->sg = wa_xfer_create_subset_sg( - xfer->urb->sg, - buf_itr_offset, buf_itr_size, - &(seg->dto_urb->num_sgs)); - if (!(seg->dto_urb->sg)) - result = -ENOMEM; - } - } - seg->dto_urb->transfer_buffer_length = buf_itr_size; - - return result; -} - -/* - * Allocate the segs array and initialize each of them - * - * The segments are freed by wa_xfer_destroy() when the xfer use count - * drops to zero; however, because each segment is given the same life - * cycle as the USB URB it contains, it is actually freed by - * usb_put_urb() on the contained USB URB (twisted, eh?). - */ -static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) -{ - int result, cnt, isoc_frame_offset = 0; - size_t alloc_size = sizeof(*xfer->seg[0]) - - sizeof(xfer->seg[0]->xfer_hdr) + xfer_hdr_size; - struct usb_device *usb_dev = xfer->wa->usb_dev; - const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd; - struct wa_seg *seg; - size_t buf_itr, buf_size, buf_itr_size; - - result = -ENOMEM; - xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC); - if (xfer->seg == NULL) - goto error_segs_kzalloc; - buf_itr = 0; - buf_size = xfer->urb->transfer_buffer_length; - for (cnt = 0; cnt < xfer->segs; cnt++) { - size_t iso_pkt_descr_size = 0; - int seg_isoc_frame_count = 0, seg_isoc_size = 0; - - /* - * Adjust the size of the segment object to contain space for - * the isoc packet descriptor buffer. - */ - if (usb_pipeisoc(xfer->urb->pipe)) { - seg_isoc_frame_count = - __wa_seg_calculate_isoc_frame_count(xfer, - isoc_frame_offset, &seg_isoc_size); - - iso_pkt_descr_size = - sizeof(struct wa_xfer_packet_info_hwaiso) + - (seg_isoc_frame_count * sizeof(__le16)); - } - result = -ENOMEM; - seg = xfer->seg[cnt] = kmalloc(alloc_size + iso_pkt_descr_size, - GFP_ATOMIC); - if (seg == NULL) - goto error_seg_kmalloc; - wa_seg_init(seg); - seg->xfer = xfer; - seg->index = cnt; - usb_fill_bulk_urb(&seg->tr_urb, usb_dev, - usb_sndbulkpipe(usb_dev, - dto_epd->bEndpointAddress), - &seg->xfer_hdr, xfer_hdr_size, - wa_seg_tr_cb, seg); - buf_itr_size = min(buf_size, xfer->seg_size); - - if (usb_pipeisoc(xfer->urb->pipe)) { - seg->isoc_frame_count = seg_isoc_frame_count; - seg->isoc_frame_offset = isoc_frame_offset; - seg->isoc_size = seg_isoc_size; - /* iso packet descriptor. */ - seg->isoc_pack_desc_urb = - usb_alloc_urb(0, GFP_ATOMIC); - if (seg->isoc_pack_desc_urb == NULL) - goto error_iso_pack_desc_alloc; - /* - * The buffer for the isoc packet descriptor starts - * after the transfer request header in the - * segment object memory buffer. - */ - usb_fill_bulk_urb( - seg->isoc_pack_desc_urb, usb_dev, - usb_sndbulkpipe(usb_dev, - dto_epd->bEndpointAddress), - (void *)(&seg->xfer_hdr) + - xfer_hdr_size, - iso_pkt_descr_size, - wa_seg_iso_pack_desc_cb, seg); - - /* adjust starting frame offset for next seg. */ - isoc_frame_offset += seg_isoc_frame_count; - } - - if (xfer->is_inbound == 0 && buf_size > 0) { - /* outbound data. */ - seg->dto_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (seg->dto_urb == NULL) - goto error_dto_alloc; - usb_fill_bulk_urb( - seg->dto_urb, usb_dev, - usb_sndbulkpipe(usb_dev, - dto_epd->bEndpointAddress), - NULL, 0, wa_seg_dto_cb, seg); - - if (usb_pipeisoc(xfer->urb->pipe)) { - /* - * Fill in the xfer buffer information for the - * first isoc frame. Subsequent frames in this - * segment will be filled in and sent from the - * DTO completion routine, if needed. - */ - __wa_populate_dto_urb_isoc(xfer, seg, - seg->isoc_frame_offset); - } else { - /* fill in the xfer buffer information. */ - result = __wa_populate_dto_urb(xfer, seg, - buf_itr, buf_itr_size); - if (result < 0) - goto error_seg_outbound_populate; - - buf_itr += buf_itr_size; - buf_size -= buf_itr_size; - } - } - seg->status = WA_SEG_READY; - } - return 0; - - /* - * Free the memory for the current segment which failed to init. - * Use the fact that cnt is left at were it failed. The remaining - * segments will be cleaned up by wa_xfer_destroy. - */ -error_seg_outbound_populate: - usb_free_urb(xfer->seg[cnt]->dto_urb); -error_dto_alloc: - usb_free_urb(xfer->seg[cnt]->isoc_pack_desc_urb); -error_iso_pack_desc_alloc: - kfree(xfer->seg[cnt]); - xfer->seg[cnt] = NULL; -error_seg_kmalloc: -error_segs_kzalloc: - return result; -} - -/* - * Allocates all the stuff needed to submit a transfer - * - * Breaks the whole data buffer in a list of segments, each one has a - * structure allocated to it and linked in xfer->seg[index] - * - * FIXME: merge setup_segs() and the last part of this function, no - * need to do two for loops when we could run everything in a - * single one - */ -static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb) -{ - int result; - struct device *dev = &xfer->wa->usb_iface->dev; - enum wa_xfer_type xfer_type = 0; /* shut up GCC */ - size_t xfer_hdr_size, cnt, transfer_size; - struct wa_xfer_hdr *xfer_hdr0, *xfer_hdr; - - result = __wa_xfer_setup_sizes(xfer, &xfer_type); - if (result < 0) - goto error_setup_sizes; - xfer_hdr_size = result; - result = __wa_xfer_setup_segs(xfer, xfer_hdr_size); - if (result < 0) { - dev_err(dev, "xfer %p: Failed to allocate %d segments: %d\n", - xfer, xfer->segs, result); - goto error_setup_segs; - } - /* Fill the first header */ - xfer_hdr0 = &xfer->seg[0]->xfer_hdr; - wa_xfer_id_init(xfer); - __wa_xfer_setup_hdr0(xfer, xfer_hdr0, xfer_type, xfer_hdr_size); - - /* Fill remaining headers */ - xfer_hdr = xfer_hdr0; - if (xfer_type == WA_XFER_TYPE_ISO) { - xfer_hdr0->dwTransferLength = - cpu_to_le32(xfer->seg[0]->isoc_size); - for (cnt = 1; cnt < xfer->segs; cnt++) { - struct wa_xfer_packet_info_hwaiso *packet_desc; - struct wa_seg *seg = xfer->seg[cnt]; - struct wa_xfer_hwaiso *xfer_iso; - - xfer_hdr = &seg->xfer_hdr; - xfer_iso = container_of(xfer_hdr, - struct wa_xfer_hwaiso, hdr); - packet_desc = ((void *)xfer_hdr) + xfer_hdr_size; - /* - * Copy values from the 0th header. Segment specific - * values are set below. - */ - memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size); - xfer_hdr->bTransferSegment = cnt; - xfer_hdr->dwTransferLength = - cpu_to_le32(seg->isoc_size); - xfer_iso->dwNumOfPackets = - cpu_to_le32(seg->isoc_frame_count); - __wa_setup_isoc_packet_descr(packet_desc, xfer, seg); - seg->status = WA_SEG_READY; - } - } else { - transfer_size = urb->transfer_buffer_length; - xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ? - cpu_to_le32(xfer->seg_size) : - cpu_to_le32(transfer_size); - transfer_size -= xfer->seg_size; - for (cnt = 1; cnt < xfer->segs; cnt++) { - xfer_hdr = &xfer->seg[cnt]->xfer_hdr; - memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size); - xfer_hdr->bTransferSegment = cnt; - xfer_hdr->dwTransferLength = - transfer_size > xfer->seg_size ? - cpu_to_le32(xfer->seg_size) - : cpu_to_le32(transfer_size); - xfer->seg[cnt]->status = WA_SEG_READY; - transfer_size -= xfer->seg_size; - } - } - xfer_hdr->bTransferSegment |= 0x80; /* this is the last segment */ - result = 0; -error_setup_segs: -error_setup_sizes: - return result; -} - -/* - * - * - * rpipe->seg_lock is held! - */ -static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, - struct wa_seg *seg, int *dto_done) -{ - int result; - - /* default to done unless we encounter a multi-frame isoc segment. */ - *dto_done = 1; - - /* - * Take a ref for each segment urb so the xfer cannot disappear until - * all of the callbacks run. - */ - wa_xfer_get(xfer); - /* submit the transfer request. */ - seg->status = WA_SEG_SUBMITTED; - result = usb_submit_urb(&seg->tr_urb, GFP_ATOMIC); - if (result < 0) { - pr_err("%s: xfer %p#%u: REQ submit failed: %d\n", - __func__, xfer, seg->index, result); - wa_xfer_put(xfer); - goto error_tr_submit; - } - /* submit the isoc packet descriptor if present. */ - if (seg->isoc_pack_desc_urb) { - wa_xfer_get(xfer); - result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC); - seg->isoc_frame_index = 0; - if (result < 0) { - pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n", - __func__, xfer, seg->index, result); - wa_xfer_put(xfer); - goto error_iso_pack_desc_submit; - } - } - /* submit the out data if this is an out request. */ - if (seg->dto_urb) { - struct wahc *wa = xfer->wa; - wa_xfer_get(xfer); - result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); - if (result < 0) { - pr_err("%s: xfer %p#%u: DTO submit failed: %d\n", - __func__, xfer, seg->index, result); - wa_xfer_put(xfer); - goto error_dto_submit; - } - /* - * If this segment contains more than one isoc frame, hold - * onto the dto resource until we send all frames. - * Only applies to non-Alereon devices. - */ - if (((wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) == 0) - && (seg->isoc_frame_count > 1)) - *dto_done = 0; - } - rpipe_avail_dec(rpipe); - return 0; - -error_dto_submit: - usb_unlink_urb(seg->isoc_pack_desc_urb); -error_iso_pack_desc_submit: - usb_unlink_urb(&seg->tr_urb); -error_tr_submit: - seg->status = WA_SEG_ERROR; - seg->result = result; - *dto_done = 1; - return result; -} - -/* - * Execute more queued request segments until the maximum concurrent allowed. - * Return true if the DTO resource was acquired and released. - * - * The ugly unlock/lock sequence on the error path is needed as the - * xfer->lock normally nests the seg_lock and not viceversa. - */ -static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting) -{ - int result, dto_acquired = 0, dto_done = 0; - struct device *dev = &rpipe->wa->usb_iface->dev; - struct wa_seg *seg; - struct wa_xfer *xfer; - unsigned long flags; - - *dto_waiting = 0; - - spin_lock_irqsave(&rpipe->seg_lock, flags); - while (atomic_read(&rpipe->segs_available) > 0 - && !list_empty(&rpipe->seg_list) - && (dto_acquired = __wa_dto_try_get(rpipe->wa))) { - seg = list_first_entry(&(rpipe->seg_list), struct wa_seg, - list_node); - list_del(&seg->list_node); - xfer = seg->xfer; - /* - * Get a reference to the xfer in case the callbacks for the - * URBs submitted by __wa_seg_submit attempt to complete - * the xfer before this function completes. - */ - wa_xfer_get(xfer); - result = __wa_seg_submit(rpipe, xfer, seg, &dto_done); - /* release the dto resource if this RPIPE is done with it. */ - if (dto_done) - __wa_dto_put(rpipe->wa); - dev_dbg(dev, "xfer %p ID %08X#%u submitted from delayed [%d segments available] %d\n", - xfer, wa_xfer_id(xfer), seg->index, - atomic_read(&rpipe->segs_available), result); - if (unlikely(result < 0)) { - int done; - - spin_unlock_irqrestore(&rpipe->seg_lock, flags); - spin_lock_irqsave(&xfer->lock, flags); - __wa_xfer_abort(xfer); - /* - * This seg was marked as submitted when it was put on - * the RPIPE seg_list. Mark it done. - */ - xfer->segs_done++; - done = __wa_xfer_is_done(xfer); - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - spin_lock_irqsave(&rpipe->seg_lock, flags); - } - wa_xfer_put(xfer); - } - /* - * Mark this RPIPE as waiting if dto was not acquired, there are - * delayed segs and no active transfers to wake us up later. - */ - if (!dto_acquired && !list_empty(&rpipe->seg_list) - && (atomic_read(&rpipe->segs_available) == - le16_to_cpu(rpipe->descr.wRequests))) - *dto_waiting = 1; - - spin_unlock_irqrestore(&rpipe->seg_lock, flags); - - return dto_done; -} - -static void wa_xfer_delayed_run(struct wa_rpipe *rpipe) -{ - int dto_waiting; - int dto_done = __wa_xfer_delayed_run(rpipe, &dto_waiting); - - /* - * If this RPIPE is waiting on the DTO resource, add it to the tail of - * the waiting list. - * Otherwise, if the WA DTO resource was acquired and released by - * __wa_xfer_delayed_run, another RPIPE may have attempted to acquire - * DTO and failed during that time. Check the delayed list and process - * any waiters. Start searching from the next RPIPE index. - */ - if (dto_waiting) - wa_add_delayed_rpipe(rpipe->wa, rpipe); - else if (dto_done) - wa_check_for_delayed_rpipes(rpipe->wa); -} - -/* - * - * xfer->lock is taken - * - * On failure submitting we just stop submitting and return error; - * wa_urb_enqueue_b() will execute the completion path - */ -static int __wa_xfer_submit(struct wa_xfer *xfer) -{ - int result, dto_acquired = 0, dto_done = 0, dto_waiting = 0; - struct wahc *wa = xfer->wa; - struct device *dev = &wa->usb_iface->dev; - unsigned cnt; - struct wa_seg *seg; - unsigned long flags; - struct wa_rpipe *rpipe = xfer->ep->hcpriv; - size_t maxrequests = le16_to_cpu(rpipe->descr.wRequests); - u8 available; - u8 empty; - - spin_lock_irqsave(&wa->xfer_list_lock, flags); - list_add_tail(&xfer->list_node, &wa->xfer_list); - spin_unlock_irqrestore(&wa->xfer_list_lock, flags); - - BUG_ON(atomic_read(&rpipe->segs_available) > maxrequests); - result = 0; - spin_lock_irqsave(&rpipe->seg_lock, flags); - for (cnt = 0; cnt < xfer->segs; cnt++) { - int delay_seg = 1; - - available = atomic_read(&rpipe->segs_available); - empty = list_empty(&rpipe->seg_list); - seg = xfer->seg[cnt]; - if (available && empty) { - /* - * Only attempt to acquire DTO if we have a segment - * to send. - */ - dto_acquired = __wa_dto_try_get(rpipe->wa); - if (dto_acquired) { - delay_seg = 0; - result = __wa_seg_submit(rpipe, xfer, seg, - &dto_done); - dev_dbg(dev, "xfer %p ID 0x%08X#%u: available %u empty %u submitted\n", - xfer, wa_xfer_id(xfer), cnt, available, - empty); - if (dto_done) - __wa_dto_put(rpipe->wa); - - if (result < 0) { - __wa_xfer_abort(xfer); - goto error_seg_submit; - } - } - } - - if (delay_seg) { - dev_dbg(dev, "xfer %p ID 0x%08X#%u: available %u empty %u delayed\n", - xfer, wa_xfer_id(xfer), cnt, available, empty); - seg->status = WA_SEG_DELAYED; - list_add_tail(&seg->list_node, &rpipe->seg_list); - } - xfer->segs_submitted++; - } -error_seg_submit: - /* - * Mark this RPIPE as waiting if dto was not acquired, there are - * delayed segs and no active transfers to wake us up later. - */ - if (!dto_acquired && !list_empty(&rpipe->seg_list) - && (atomic_read(&rpipe->segs_available) == - le16_to_cpu(rpipe->descr.wRequests))) - dto_waiting = 1; - spin_unlock_irqrestore(&rpipe->seg_lock, flags); - - if (dto_waiting) - wa_add_delayed_rpipe(rpipe->wa, rpipe); - else if (dto_done) - wa_check_for_delayed_rpipes(rpipe->wa); - - return result; -} - -/* - * Second part of a URB/transfer enqueuement - * - * Assumes this comes from wa_urb_enqueue() [maybe through - * wa_urb_enqueue_run()]. At this point: - * - * xfer->wa filled and refcounted - * xfer->ep filled with rpipe refcounted if - * delayed == 0 - * xfer->urb filled and refcounted (this is the case when called - * from wa_urb_enqueue() as we come from usb_submit_urb() - * and when called by wa_urb_enqueue_run(), as we took an - * extra ref dropped by _run() after we return). - * xfer->gfp filled - * - * If we fail at __wa_xfer_submit(), then we just check if we are done - * and if so, we run the completion procedure. However, if we are not - * yet done, we do nothing and wait for the completion handlers from - * the submitted URBs or from the xfer-result path to kick in. If xfer - * result never kicks in, the xfer will timeout from the USB code and - * dequeue() will be called. - */ -static int wa_urb_enqueue_b(struct wa_xfer *xfer) -{ - int result; - unsigned long flags; - struct urb *urb = xfer->urb; - struct wahc *wa = xfer->wa; - struct wusbhc *wusbhc = wa->wusb; - struct wusb_dev *wusb_dev; - unsigned done; - - result = rpipe_get_by_ep(wa, xfer->ep, urb, xfer->gfp); - if (result < 0) { - pr_err("%s: error_rpipe_get\n", __func__); - goto error_rpipe_get; - } - result = -ENODEV; - /* FIXME: segmentation broken -- kills DWA */ - mutex_lock(&wusbhc->mutex); /* get a WUSB dev */ - if (urb->dev == NULL) { - mutex_unlock(&wusbhc->mutex); - pr_err("%s: error usb dev gone\n", __func__); - goto error_dev_gone; - } - wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev); - if (wusb_dev == NULL) { - mutex_unlock(&wusbhc->mutex); - dev_err(&(urb->dev->dev), "%s: error wusb dev gone\n", - __func__); - goto error_dev_gone; - } - mutex_unlock(&wusbhc->mutex); - - spin_lock_irqsave(&xfer->lock, flags); - xfer->wusb_dev = wusb_dev; - result = urb->status; - if (urb->status != -EINPROGRESS) { - dev_err(&(urb->dev->dev), "%s: error_dequeued\n", __func__); - goto error_dequeued; - } - - result = __wa_xfer_setup(xfer, urb); - if (result < 0) { - dev_err(&(urb->dev->dev), "%s: error_xfer_setup\n", __func__); - goto error_xfer_setup; - } - /* - * Get a xfer reference since __wa_xfer_submit starts asynchronous - * operations that may try to complete the xfer before this function - * exits. - */ - wa_xfer_get(xfer); - result = __wa_xfer_submit(xfer); - if (result < 0) { - dev_err(&(urb->dev->dev), "%s: error_xfer_submit\n", __func__); - goto error_xfer_submit; - } - spin_unlock_irqrestore(&xfer->lock, flags); - wa_xfer_put(xfer); - return 0; - - /* - * this is basically wa_xfer_completion() broken up wa_xfer_giveback() - * does a wa_xfer_put() that will call wa_xfer_destroy() and undo - * setup(). - */ -error_xfer_setup: -error_dequeued: - spin_unlock_irqrestore(&xfer->lock, flags); - /* FIXME: segmentation broken, kills DWA */ - if (wusb_dev) - wusb_dev_put(wusb_dev); -error_dev_gone: - rpipe_put(xfer->ep->hcpriv); -error_rpipe_get: - xfer->result = result; - return result; - -error_xfer_submit: - done = __wa_xfer_is_done(xfer); - xfer->result = result; - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - wa_xfer_put(xfer); - /* return success since the completion routine will run. */ - return 0; -} - -/* - * Execute the delayed transfers in the Wire Adapter @wa - * - * We need to be careful here, as dequeue() could be called in the - * middle. That's why we do the whole thing under the - * wa->xfer_list_lock. If dequeue() jumps in, it first locks xfer->lock - * and then checks the list -- so as we would be acquiring in inverse - * order, we move the delayed list to a separate list while locked and then - * submit them without the list lock held. - */ -void wa_urb_enqueue_run(struct work_struct *ws) -{ - struct wahc *wa = container_of(ws, struct wahc, xfer_enqueue_work); - struct wa_xfer *xfer, *next; - struct urb *urb; - LIST_HEAD(tmp_list); - - /* Create a copy of the wa->xfer_delayed_list while holding the lock */ - spin_lock_irq(&wa->xfer_list_lock); - list_cut_position(&tmp_list, &wa->xfer_delayed_list, - wa->xfer_delayed_list.prev); - spin_unlock_irq(&wa->xfer_list_lock); - - /* - * enqueue from temp list without list lock held since wa_urb_enqueue_b - * can take xfer->lock as well as lock mutexes. - */ - list_for_each_entry_safe(xfer, next, &tmp_list, list_node) { - list_del_init(&xfer->list_node); - - urb = xfer->urb; - if (wa_urb_enqueue_b(xfer) < 0) - wa_xfer_giveback(xfer); - usb_put_urb(urb); /* taken when queuing */ - } -} -EXPORT_SYMBOL_GPL(wa_urb_enqueue_run); - -/* - * Process the errored transfers on the Wire Adapter outside of interrupt. - */ -void wa_process_errored_transfers_run(struct work_struct *ws) -{ - struct wahc *wa = container_of(ws, struct wahc, xfer_error_work); - struct wa_xfer *xfer, *next; - LIST_HEAD(tmp_list); - - pr_info("%s: Run delayed STALL processing.\n", __func__); - - /* Create a copy of the wa->xfer_errored_list while holding the lock */ - spin_lock_irq(&wa->xfer_list_lock); - list_cut_position(&tmp_list, &wa->xfer_errored_list, - wa->xfer_errored_list.prev); - spin_unlock_irq(&wa->xfer_list_lock); - - /* - * run rpipe_clear_feature_stalled from temp list without list lock - * held. - */ - list_for_each_entry_safe(xfer, next, &tmp_list, list_node) { - struct usb_host_endpoint *ep; - unsigned long flags; - struct wa_rpipe *rpipe; - - spin_lock_irqsave(&xfer->lock, flags); - ep = xfer->ep; - rpipe = ep->hcpriv; - spin_unlock_irqrestore(&xfer->lock, flags); - - /* clear RPIPE feature stalled without holding a lock. */ - rpipe_clear_feature_stalled(wa, ep); - - /* complete the xfer. This removes it from the tmp list. */ - wa_xfer_completion(xfer); - - /* check for work. */ - wa_xfer_delayed_run(rpipe); - } -} -EXPORT_SYMBOL_GPL(wa_process_errored_transfers_run); - -/* - * Submit a transfer to the Wire Adapter in a delayed way - * - * The process of enqueuing involves possible sleeps() [see - * enqueue_b(), for the rpipe_get() and the mutex_lock()]. If we are - * in an atomic section, we defer the enqueue_b() call--else we call direct. - * - * @urb: We own a reference to it done by the HCI Linux USB stack that - * will be given up by calling usb_hcd_giveback_urb() or by - * returning error from this function -> ergo we don't have to - * refcount it. - */ -int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep, - struct urb *urb, gfp_t gfp) -{ - int result; - struct device *dev = &wa->usb_iface->dev; - struct wa_xfer *xfer; - unsigned long my_flags; - unsigned cant_sleep = irqs_disabled() | in_atomic(); - - if ((urb->transfer_buffer == NULL) - && (urb->sg == NULL) - && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) - && urb->transfer_buffer_length != 0) { - dev_err(dev, "BUG? urb %p: NULL xfer buffer & NODMA\n", urb); - dump_stack(); - } - - spin_lock_irqsave(&wa->xfer_list_lock, my_flags); - result = usb_hcd_link_urb_to_ep(&(wa->wusb->usb_hcd), urb); - spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); - if (result < 0) - goto error_link_urb; - - result = -ENOMEM; - xfer = kzalloc(sizeof(*xfer), gfp); - if (xfer == NULL) - goto error_kmalloc; - - result = -ENOENT; - if (urb->status != -EINPROGRESS) /* cancelled */ - goto error_dequeued; /* before starting? */ - wa_xfer_init(xfer); - xfer->wa = wa_get(wa); - xfer->urb = urb; - xfer->gfp = gfp; - xfer->ep = ep; - urb->hcpriv = xfer; - - dev_dbg(dev, "xfer %p urb %p pipe 0x%02x [%d bytes] %s %s %s\n", - xfer, urb, urb->pipe, urb->transfer_buffer_length, - urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? "dma" : "nodma", - urb->pipe & USB_DIR_IN ? "inbound" : "outbound", - cant_sleep ? "deferred" : "inline"); - - if (cant_sleep) { - usb_get_urb(urb); - spin_lock_irqsave(&wa->xfer_list_lock, my_flags); - list_add_tail(&xfer->list_node, &wa->xfer_delayed_list); - spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); - queue_work(wusbd, &wa->xfer_enqueue_work); - } else { - result = wa_urb_enqueue_b(xfer); - if (result < 0) { - /* - * URB submit/enqueue failed. Clean up, return an - * error and do not run the callback. This avoids - * an infinite submit/complete loop. - */ - dev_err(dev, "%s: URB enqueue failed: %d\n", - __func__, result); - wa_put(xfer->wa); - wa_xfer_put(xfer); - spin_lock_irqsave(&wa->xfer_list_lock, my_flags); - usb_hcd_unlink_urb_from_ep(&(wa->wusb->usb_hcd), urb); - spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); - return result; - } - } - return 0; - -error_dequeued: - kfree(xfer); -error_kmalloc: - spin_lock_irqsave(&wa->xfer_list_lock, my_flags); - usb_hcd_unlink_urb_from_ep(&(wa->wusb->usb_hcd), urb); - spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); -error_link_urb: - return result; -} -EXPORT_SYMBOL_GPL(wa_urb_enqueue); - -/* - * Dequeue a URB and make sure uwb_hcd_giveback_urb() [completion - * handler] is called. - * - * Until a transfer goes successfully through wa_urb_enqueue() it - * needs to be dequeued with completion calling; when stuck in delayed - * or before wa_xfer_setup() is called, we need to do completion. - * - * not setup If there is no hcpriv yet, that means that that enqueue - * still had no time to set the xfer up. Because - * urb->status should be other than -EINPROGRESS, - * enqueue() will catch that and bail out. - * - * If the transfer has gone through setup, we just need to clean it - * up. If it has gone through submit(), we have to abort it [with an - * asynch request] and then make sure we cancel each segment. - * - */ -int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status) -{ - unsigned long flags; - struct wa_xfer *xfer; - struct wa_seg *seg; - struct wa_rpipe *rpipe; - unsigned cnt, done = 0, xfer_abort_pending; - unsigned rpipe_ready = 0; - int result; - - /* check if it is safe to unlink. */ - spin_lock_irqsave(&wa->xfer_list_lock, flags); - result = usb_hcd_check_unlink_urb(&(wa->wusb->usb_hcd), urb, status); - if ((result == 0) && urb->hcpriv) { - /* - * Get a xfer ref to prevent a race with wa_xfer_giveback - * cleaning up the xfer while we are working with it. - */ - wa_xfer_get(urb->hcpriv); - } - spin_unlock_irqrestore(&wa->xfer_list_lock, flags); - if (result) - return result; - - xfer = urb->hcpriv; - if (xfer == NULL) - return -ENOENT; - spin_lock_irqsave(&xfer->lock, flags); - pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer)); - rpipe = xfer->ep->hcpriv; - if (rpipe == NULL) { - pr_debug("%s: xfer %p id 0x%08X has no RPIPE. %s", - __func__, xfer, wa_xfer_id(xfer), - "Probably already aborted.\n" ); - result = -ENOENT; - goto out_unlock; - } - /* - * Check for done to avoid racing with wa_xfer_giveback and completing - * twice. - */ - if (__wa_xfer_is_done(xfer)) { - pr_debug("%s: xfer %p id 0x%08X already done.\n", __func__, - xfer, wa_xfer_id(xfer)); - result = -ENOENT; - goto out_unlock; - } - /* Check the delayed list -> if there, release and complete */ - spin_lock(&wa->xfer_list_lock); - if (!list_empty(&xfer->list_node) && xfer->seg == NULL) - goto dequeue_delayed; - spin_unlock(&wa->xfer_list_lock); - if (xfer->seg == NULL) /* still hasn't reached */ - goto out_unlock; /* setup(), enqueue_b() completes */ - /* Ok, the xfer is in flight already, it's been setup and submitted.*/ - xfer_abort_pending = __wa_xfer_abort(xfer) >= 0; - /* - * grab the rpipe->seg_lock here to prevent racing with - * __wa_xfer_delayed_run. - */ - spin_lock(&rpipe->seg_lock); - for (cnt = 0; cnt < xfer->segs; cnt++) { - seg = xfer->seg[cnt]; - pr_debug("%s: xfer id 0x%08X#%d status = %d\n", - __func__, wa_xfer_id(xfer), cnt, seg->status); - switch (seg->status) { - case WA_SEG_NOTREADY: - case WA_SEG_READY: - printk(KERN_ERR "xfer %p#%u: dequeue bad state %u\n", - xfer, cnt, seg->status); - WARN_ON(1); - break; - case WA_SEG_DELAYED: - /* - * delete from rpipe delayed list. If no segments on - * this xfer have been submitted, __wa_xfer_is_done will - * trigger a giveback below. Otherwise, the submitted - * segments will be completed in the DTI interrupt. - */ - seg->status = WA_SEG_ABORTED; - seg->result = -ENOENT; - list_del(&seg->list_node); - xfer->segs_done++; - break; - case WA_SEG_DONE: - case WA_SEG_ERROR: - case WA_SEG_ABORTED: - break; - /* - * The buf_in data for a segment in the - * WA_SEG_DTI_PENDING state is actively being read. - * Let wa_buf_in_cb handle it since it will be called - * and will increment xfer->segs_done. Cleaning up - * here could cause wa_buf_in_cb to access the xfer - * after it has been completed/freed. - */ - case WA_SEG_DTI_PENDING: - break; - /* - * In the states below, the HWA device already knows - * about the transfer. If an abort request was sent, - * allow the HWA to process it and wait for the - * results. Otherwise, the DTI state and seg completed - * counts can get out of sync. - */ - case WA_SEG_SUBMITTED: - case WA_SEG_PENDING: - /* - * Check if the abort was successfully sent. This could - * be false if the HWA has been removed but we haven't - * gotten the disconnect notification yet. - */ - if (!xfer_abort_pending) { - seg->status = WA_SEG_ABORTED; - rpipe_ready = rpipe_avail_inc(rpipe); - xfer->segs_done++; - } - break; - } - } - spin_unlock(&rpipe->seg_lock); - xfer->result = urb->status; /* -ENOENT or -ECONNRESET */ - done = __wa_xfer_is_done(xfer); - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - wa_xfer_put(xfer); - return result; - -out_unlock: - spin_unlock_irqrestore(&xfer->lock, flags); - wa_xfer_put(xfer); - return result; - -dequeue_delayed: - list_del_init(&xfer->list_node); - spin_unlock(&wa->xfer_list_lock); - xfer->result = urb->status; - spin_unlock_irqrestore(&xfer->lock, flags); - wa_xfer_giveback(xfer); - wa_xfer_put(xfer); - usb_put_urb(urb); /* we got a ref in enqueue() */ - return 0; -} -EXPORT_SYMBOL_GPL(wa_urb_dequeue); - -/* - * Translation from WA status codes (WUSB1.0 Table 8.15) to errno - * codes - * - * Positive errno values are internal inconsistencies and should be - * flagged louder. Negative are to be passed up to the user in the - * normal way. - * - * @status: USB WA status code -- high two bits are stripped. - */ -static int wa_xfer_status_to_errno(u8 status) -{ - int errno; - u8 real_status = status; - static int xlat[] = { - [WA_XFER_STATUS_SUCCESS] = 0, - [WA_XFER_STATUS_HALTED] = -EPIPE, - [WA_XFER_STATUS_DATA_BUFFER_ERROR] = -ENOBUFS, - [WA_XFER_STATUS_BABBLE] = -EOVERFLOW, - [WA_XFER_RESERVED] = EINVAL, - [WA_XFER_STATUS_NOT_FOUND] = 0, - [WA_XFER_STATUS_INSUFFICIENT_RESOURCE] = -ENOMEM, - [WA_XFER_STATUS_TRANSACTION_ERROR] = -EILSEQ, - [WA_XFER_STATUS_ABORTED] = -ENOENT, - [WA_XFER_STATUS_RPIPE_NOT_READY] = EINVAL, - [WA_XFER_INVALID_FORMAT] = EINVAL, - [WA_XFER_UNEXPECTED_SEGMENT_NUMBER] = EINVAL, - [WA_XFER_STATUS_RPIPE_TYPE_MISMATCH] = EINVAL, - }; - status &= 0x3f; - - if (status == 0) - return 0; - if (status >= ARRAY_SIZE(xlat)) { - printk_ratelimited(KERN_ERR "%s(): BUG? " - "Unknown WA transfer status 0x%02x\n", - __func__, real_status); - return -EINVAL; - } - errno = xlat[status]; - if (unlikely(errno > 0)) { - printk_ratelimited(KERN_ERR "%s(): BUG? " - "Inconsistent WA status: 0x%02x\n", - __func__, real_status); - errno = -errno; - } - return errno; -} - -/* - * If a last segment flag and/or a transfer result error is encountered, - * no other segment transfer results will be returned from the device. - * Mark the remaining submitted or pending xfers as completed so that - * the xfer will complete cleanly. - * - * xfer->lock must be held - * - */ -static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer, - int starting_index, enum wa_seg_status status) -{ - int index; - struct wa_rpipe *rpipe = xfer->ep->hcpriv; - - for (index = starting_index; index < xfer->segs_submitted; index++) { - struct wa_seg *current_seg = xfer->seg[index]; - - BUG_ON(current_seg == NULL); - - switch (current_seg->status) { - case WA_SEG_SUBMITTED: - case WA_SEG_PENDING: - case WA_SEG_DTI_PENDING: - rpipe_avail_inc(rpipe); - /* - * do not increment RPIPE avail for the WA_SEG_DELAYED case - * since it has not been submitted to the RPIPE. - */ - /* fall through */ - case WA_SEG_DELAYED: - xfer->segs_done++; - current_seg->status = status; - break; - case WA_SEG_ABORTED: - break; - default: - WARN(1, "%s: xfer 0x%08X#%d. bad seg status = %d\n", - __func__, wa_xfer_id(xfer), index, - current_seg->status); - break; - } - } -} - -/* Populate the given urb based on the current isoc transfer state. */ -static int __wa_populate_buf_in_urb_isoc(struct wahc *wa, - struct urb *buf_in_urb, struct wa_xfer *xfer, struct wa_seg *seg) -{ - int urb_start_frame = seg->isoc_frame_index + seg->isoc_frame_offset; - int seg_index, total_len = 0, urb_frame_index = urb_start_frame; - struct usb_iso_packet_descriptor *iso_frame_desc = - xfer->urb->iso_frame_desc; - const int dti_packet_size = usb_endpoint_maxp(wa->dti_epd); - int next_frame_contiguous; - struct usb_iso_packet_descriptor *iso_frame; - - BUG_ON(buf_in_urb->status == -EINPROGRESS); - - /* - * If the current frame actual_length is contiguous with the next frame - * and actual_length is a multiple of the DTI endpoint max packet size, - * combine the current frame with the next frame in a single URB. This - * reduces the number of URBs that must be submitted in that case. - */ - seg_index = seg->isoc_frame_index; - do { - next_frame_contiguous = 0; - - iso_frame = &iso_frame_desc[urb_frame_index]; - total_len += iso_frame->actual_length; - ++urb_frame_index; - ++seg_index; - - if (seg_index < seg->isoc_frame_count) { - struct usb_iso_packet_descriptor *next_iso_frame; - - next_iso_frame = &iso_frame_desc[urb_frame_index]; - - if ((iso_frame->offset + iso_frame->actual_length) == - next_iso_frame->offset) - next_frame_contiguous = 1; - } - } while (next_frame_contiguous - && ((iso_frame->actual_length % dti_packet_size) == 0)); - - /* this should always be 0 before a resubmit. */ - buf_in_urb->num_mapped_sgs = 0; - buf_in_urb->transfer_dma = xfer->urb->transfer_dma + - iso_frame_desc[urb_start_frame].offset; - buf_in_urb->transfer_buffer_length = total_len; - buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - buf_in_urb->transfer_buffer = NULL; - buf_in_urb->sg = NULL; - buf_in_urb->num_sgs = 0; - buf_in_urb->context = seg; - - /* return the number of frames included in this URB. */ - return seg_index - seg->isoc_frame_index; -} - -/* Populate the given urb based on the current transfer state. */ -static int wa_populate_buf_in_urb(struct urb *buf_in_urb, struct wa_xfer *xfer, - unsigned int seg_idx, unsigned int bytes_transferred) -{ - int result = 0; - struct wa_seg *seg = xfer->seg[seg_idx]; - - BUG_ON(buf_in_urb->status == -EINPROGRESS); - /* this should always be 0 before a resubmit. */ - buf_in_urb->num_mapped_sgs = 0; - - if (xfer->is_dma) { - buf_in_urb->transfer_dma = xfer->urb->transfer_dma - + (seg_idx * xfer->seg_size); - buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - buf_in_urb->transfer_buffer = NULL; - buf_in_urb->sg = NULL; - buf_in_urb->num_sgs = 0; - } else { - /* do buffer or SG processing. */ - buf_in_urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP; - - if (xfer->urb->transfer_buffer) { - buf_in_urb->transfer_buffer = - xfer->urb->transfer_buffer - + (seg_idx * xfer->seg_size); - buf_in_urb->sg = NULL; - buf_in_urb->num_sgs = 0; - } else { - /* allocate an SG list to store seg_size bytes - and copy the subset of the xfer->urb->sg - that matches the buffer subset we are - about to read. */ - buf_in_urb->sg = wa_xfer_create_subset_sg( - xfer->urb->sg, - seg_idx * xfer->seg_size, - bytes_transferred, - &(buf_in_urb->num_sgs)); - - if (!(buf_in_urb->sg)) { - buf_in_urb->num_sgs = 0; - result = -ENOMEM; - } - buf_in_urb->transfer_buffer = NULL; - } - } - buf_in_urb->transfer_buffer_length = bytes_transferred; - buf_in_urb->context = seg; - - return result; -} - -/* - * Process a xfer result completion message - * - * inbound transfers: need to schedule a buf_in_urb read - * - * FIXME: this function needs to be broken up in parts - */ -static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer, - struct wa_xfer_result *xfer_result) -{ - int result; - struct device *dev = &wa->usb_iface->dev; - unsigned long flags; - unsigned int seg_idx; - struct wa_seg *seg; - struct wa_rpipe *rpipe; - unsigned done = 0; - u8 usb_status; - unsigned rpipe_ready = 0; - unsigned bytes_transferred = le32_to_cpu(xfer_result->dwTransferLength); - struct urb *buf_in_urb = &(wa->buf_in_urbs[0]); - - spin_lock_irqsave(&xfer->lock, flags); - seg_idx = xfer_result->bTransferSegment & 0x7f; - if (unlikely(seg_idx >= xfer->segs)) - goto error_bad_seg; - seg = xfer->seg[seg_idx]; - rpipe = xfer->ep->hcpriv; - usb_status = xfer_result->bTransferStatus; - dev_dbg(dev, "xfer %p ID 0x%08X#%u: bTransferStatus 0x%02x (seg status %u)\n", - xfer, wa_xfer_id(xfer), seg_idx, usb_status, seg->status); - if (seg->status == WA_SEG_ABORTED - || seg->status == WA_SEG_ERROR) /* already handled */ - goto segment_aborted; - if (seg->status == WA_SEG_SUBMITTED) /* ops, got here */ - seg->status = WA_SEG_PENDING; /* before wa_seg{_dto}_cb() */ - if (seg->status != WA_SEG_PENDING) { - if (printk_ratelimit()) - dev_err(dev, "xfer %p#%u: Bad segment state %u\n", - xfer, seg_idx, seg->status); - seg->status = WA_SEG_PENDING; /* workaround/"fix" it */ - } - if (usb_status & 0x80) { - seg->result = wa_xfer_status_to_errno(usb_status); - dev_err(dev, "DTI: xfer %p 0x%08X:#%u failed (0x%02x)\n", - xfer, xfer->id, seg->index, usb_status); - seg->status = ((usb_status & 0x7F) == WA_XFER_STATUS_ABORTED) ? - WA_SEG_ABORTED : WA_SEG_ERROR; - goto error_complete; - } - /* FIXME: we ignore warnings, tally them for stats */ - if (usb_status & 0x40) /* Warning?... */ - usb_status = 0; /* ... pass */ - /* - * If the last segment bit is set, complete the remaining segments. - * When the current segment is completed, either in wa_buf_in_cb for - * transfers with data or below for no data, the xfer will complete. - */ - if (xfer_result->bTransferSegment & 0x80) - wa_complete_remaining_xfer_segs(xfer, seg->index + 1, - WA_SEG_DONE); - if (usb_pipeisoc(xfer->urb->pipe) - && (le32_to_cpu(xfer_result->dwNumOfPackets) > 0)) { - /* set up WA state to read the isoc packet status next. */ - wa->dti_isoc_xfer_in_progress = wa_xfer_id(xfer); - wa->dti_isoc_xfer_seg = seg_idx; - wa->dti_state = WA_DTI_ISOC_PACKET_STATUS_PENDING; - } else if (xfer->is_inbound && !usb_pipeisoc(xfer->urb->pipe) - && (bytes_transferred > 0)) { - /* IN data phase: read to buffer */ - seg->status = WA_SEG_DTI_PENDING; - result = wa_populate_buf_in_urb(buf_in_urb, xfer, seg_idx, - bytes_transferred); - if (result < 0) - goto error_buf_in_populate; - ++(wa->active_buf_in_urbs); - result = usb_submit_urb(buf_in_urb, GFP_ATOMIC); - if (result < 0) { - --(wa->active_buf_in_urbs); - goto error_submit_buf_in; - } - } else { - /* OUT data phase or no data, complete it -- */ - seg->result = bytes_transferred; - rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE); - } - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - return; - -error_submit_buf_in: - if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { - dev_err(dev, "DTI: URB max acceptable errors " - "exceeded, resetting device\n"); - wa_reset_all(wa); - } - if (printk_ratelimit()) - dev_err(dev, "xfer %p#%u: can't submit DTI data phase: %d\n", - xfer, seg_idx, result); - seg->result = result; - kfree(buf_in_urb->sg); - buf_in_urb->sg = NULL; -error_buf_in_populate: - __wa_xfer_abort(xfer); - seg->status = WA_SEG_ERROR; -error_complete: - xfer->segs_done++; - rpipe_ready = rpipe_avail_inc(rpipe); - wa_complete_remaining_xfer_segs(xfer, seg->index + 1, seg->status); - done = __wa_xfer_is_done(xfer); - /* - * queue work item to clear STALL for control endpoints. - * Otherwise, let endpoint_reset take care of it. - */ - if (((usb_status & 0x3f) == WA_XFER_STATUS_HALTED) && - usb_endpoint_xfer_control(&xfer->ep->desc) && - done) { - - dev_info(dev, "Control EP stall. Queue delayed work.\n"); - spin_lock(&wa->xfer_list_lock); - /* move xfer from xfer_list to xfer_errored_list. */ - list_move_tail(&xfer->list_node, &wa->xfer_errored_list); - spin_unlock(&wa->xfer_list_lock); - spin_unlock_irqrestore(&xfer->lock, flags); - queue_work(wusbd, &wa->xfer_error_work); - } else { - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - } - - return; - -error_bad_seg: - spin_unlock_irqrestore(&xfer->lock, flags); - wa_urb_dequeue(wa, xfer->urb, -ENOENT); - if (printk_ratelimit()) - dev_err(dev, "xfer %p#%u: bad segment\n", xfer, seg_idx); - if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { - dev_err(dev, "DTI: URB max acceptable errors " - "exceeded, resetting device\n"); - wa_reset_all(wa); - } - return; - -segment_aborted: - /* nothing to do, as the aborter did the completion */ - spin_unlock_irqrestore(&xfer->lock, flags); -} - -/* - * Process a isochronous packet status message - * - * inbound transfers: need to schedule a buf_in_urb read - */ -static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb) -{ - struct device *dev = &wa->usb_iface->dev; - struct wa_xfer_packet_status_hwaiso *packet_status; - struct wa_xfer_packet_status_len_hwaiso *status_array; - struct wa_xfer *xfer; - unsigned long flags; - struct wa_seg *seg; - struct wa_rpipe *rpipe; - unsigned done = 0, dti_busy = 0, data_frame_count = 0, seg_index; - unsigned first_frame_index = 0, rpipe_ready = 0; - size_t expected_size; - - /* We have a xfer result buffer; check it */ - dev_dbg(dev, "DTI: isoc packet status %d bytes at %p\n", - urb->actual_length, urb->transfer_buffer); - packet_status = (struct wa_xfer_packet_status_hwaiso *)(wa->dti_buf); - if (packet_status->bPacketType != WA_XFER_ISO_PACKET_STATUS) { - dev_err(dev, "DTI Error: isoc packet status--bad type 0x%02x\n", - packet_status->bPacketType); - goto error_parse_buffer; - } - xfer = wa_xfer_get_by_id(wa, wa->dti_isoc_xfer_in_progress); - if (xfer == NULL) { - dev_err(dev, "DTI Error: isoc packet status--unknown xfer 0x%08x\n", - wa->dti_isoc_xfer_in_progress); - goto error_parse_buffer; - } - spin_lock_irqsave(&xfer->lock, flags); - if (unlikely(wa->dti_isoc_xfer_seg >= xfer->segs)) - goto error_bad_seg; - seg = xfer->seg[wa->dti_isoc_xfer_seg]; - rpipe = xfer->ep->hcpriv; - expected_size = struct_size(packet_status, PacketStatus, - seg->isoc_frame_count); - if (urb->actual_length != expected_size) { - dev_err(dev, "DTI Error: isoc packet status--bad urb length (%d bytes vs %zu needed)\n", - urb->actual_length, expected_size); - goto error_bad_seg; - } - if (le16_to_cpu(packet_status->wLength) != expected_size) { - dev_err(dev, "DTI Error: isoc packet status--bad length %u\n", - le16_to_cpu(packet_status->wLength)); - goto error_bad_seg; - } - /* write isoc packet status and lengths back to the xfer urb. */ - status_array = packet_status->PacketStatus; - xfer->urb->start_frame = - wa->wusb->usb_hcd.driver->get_frame_number(&wa->wusb->usb_hcd); - for (seg_index = 0; seg_index < seg->isoc_frame_count; ++seg_index) { - struct usb_iso_packet_descriptor *iso_frame_desc = - xfer->urb->iso_frame_desc; - const int xfer_frame_index = - seg->isoc_frame_offset + seg_index; - - iso_frame_desc[xfer_frame_index].status = - wa_xfer_status_to_errno( - le16_to_cpu(status_array[seg_index].PacketStatus)); - iso_frame_desc[xfer_frame_index].actual_length = - le16_to_cpu(status_array[seg_index].PacketLength); - /* track the number of frames successfully transferred. */ - if (iso_frame_desc[xfer_frame_index].actual_length > 0) { - /* save the starting frame index for buf_in_urb. */ - if (!data_frame_count) - first_frame_index = seg_index; - ++data_frame_count; - } - } - - if (xfer->is_inbound && data_frame_count) { - int result, total_frames_read = 0, urb_index = 0; - struct urb *buf_in_urb; - - /* IN data phase: read to buffer */ - seg->status = WA_SEG_DTI_PENDING; - - /* start with the first frame with data. */ - seg->isoc_frame_index = first_frame_index; - /* submit up to WA_MAX_BUF_IN_URBS read URBs. */ - do { - int urb_frame_index, urb_frame_count; - struct usb_iso_packet_descriptor *iso_frame_desc; - - buf_in_urb = &(wa->buf_in_urbs[urb_index]); - urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, - buf_in_urb, xfer, seg); - /* advance frame index to start of next read URB. */ - seg->isoc_frame_index += urb_frame_count; - total_frames_read += urb_frame_count; - - ++(wa->active_buf_in_urbs); - result = usb_submit_urb(buf_in_urb, GFP_ATOMIC); - - /* skip 0-byte frames. */ - urb_frame_index = - seg->isoc_frame_offset + seg->isoc_frame_index; - iso_frame_desc = - &(xfer->urb->iso_frame_desc[urb_frame_index]); - while ((seg->isoc_frame_index < - seg->isoc_frame_count) && - (iso_frame_desc->actual_length == 0)) { - ++(seg->isoc_frame_index); - ++iso_frame_desc; - } - ++urb_index; - - } while ((result == 0) && (urb_index < WA_MAX_BUF_IN_URBS) - && (seg->isoc_frame_index < - seg->isoc_frame_count)); - - if (result < 0) { - --(wa->active_buf_in_urbs); - dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", - result); - wa_reset_all(wa); - } else if (data_frame_count > total_frames_read) - /* If we need to read more frames, set DTI busy. */ - dti_busy = 1; - } else { - /* OUT transfer or no more IN data, complete it -- */ - rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE); - } - spin_unlock_irqrestore(&xfer->lock, flags); - if (dti_busy) - wa->dti_state = WA_DTI_BUF_IN_DATA_PENDING; - else - wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - wa_xfer_put(xfer); - return dti_busy; - -error_bad_seg: - spin_unlock_irqrestore(&xfer->lock, flags); - wa_xfer_put(xfer); -error_parse_buffer: - return dti_busy; -} - -/* - * Callback for the IN data phase - * - * If successful transition state; otherwise, take a note of the - * error, mark this segment done and try completion. - * - * Note we don't access until we are sure that the transfer hasn't - * been cancelled (ECONNRESET, ENOENT), which could mean that - * seg->xfer could be already gone. - */ -static void wa_buf_in_cb(struct urb *urb) -{ - struct wa_seg *seg = urb->context; - struct wa_xfer *xfer = seg->xfer; - struct wahc *wa; - struct device *dev; - struct wa_rpipe *rpipe; - unsigned rpipe_ready = 0, isoc_data_frame_count = 0; - unsigned long flags; - int resubmit_dti = 0, active_buf_in_urbs; - u8 done = 0; - - /* free the sg if it was used. */ - kfree(urb->sg); - urb->sg = NULL; - - spin_lock_irqsave(&xfer->lock, flags); - wa = xfer->wa; - dev = &wa->usb_iface->dev; - --(wa->active_buf_in_urbs); - active_buf_in_urbs = wa->active_buf_in_urbs; - rpipe = xfer->ep->hcpriv; - - if (usb_pipeisoc(xfer->urb->pipe)) { - struct usb_iso_packet_descriptor *iso_frame_desc = - xfer->urb->iso_frame_desc; - int seg_index; - - /* - * Find the next isoc frame with data and count how many - * frames with data remain. - */ - seg_index = seg->isoc_frame_index; - while (seg_index < seg->isoc_frame_count) { - const int urb_frame_index = - seg->isoc_frame_offset + seg_index; - - if (iso_frame_desc[urb_frame_index].actual_length > 0) { - /* save the index of the next frame with data */ - if (!isoc_data_frame_count) - seg->isoc_frame_index = seg_index; - ++isoc_data_frame_count; - } - ++seg_index; - } - } - spin_unlock_irqrestore(&xfer->lock, flags); - - switch (urb->status) { - case 0: - spin_lock_irqsave(&xfer->lock, flags); - - seg->result += urb->actual_length; - if (isoc_data_frame_count > 0) { - int result, urb_frame_count; - - /* submit a read URB for the next frame with data. */ - urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, urb, - xfer, seg); - /* advance index to start of next read URB. */ - seg->isoc_frame_index += urb_frame_count; - ++(wa->active_buf_in_urbs); - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result < 0) { - --(wa->active_buf_in_urbs); - dev_err(dev, "DTI Error: Could not submit buf in URB (%d)", - result); - wa_reset_all(wa); - } - /* - * If we are in this callback and - * isoc_data_frame_count > 0, it means that the dti_urb - * submission was delayed in wa_dti_cb. Once - * we submit the last buf_in_urb, we can submit the - * delayed dti_urb. - */ - resubmit_dti = (isoc_data_frame_count == - urb_frame_count); - } else if (active_buf_in_urbs == 0) { - dev_dbg(dev, - "xfer %p 0x%08X#%u: data in done (%zu bytes)\n", - xfer, wa_xfer_id(xfer), seg->index, - seg->result); - rpipe_ready = rpipe_avail_inc(rpipe); - done = __wa_xfer_mark_seg_as_done(xfer, seg, - WA_SEG_DONE); - } - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - break; - case -ECONNRESET: /* URB unlinked; no need to do anything */ - case -ENOENT: /* as it was done by the who unlinked us */ - break; - default: /* Other errors ... */ - /* - * Error on data buf read. Only resubmit DTI if it hasn't - * already been done by previously hitting this error or by a - * successful completion of the previous buf_in_urb. - */ - resubmit_dti = wa->dti_state != WA_DTI_TRANSFER_RESULT_PENDING; - spin_lock_irqsave(&xfer->lock, flags); - if (printk_ratelimit()) - dev_err(dev, "xfer %p 0x%08X#%u: data in error %d\n", - xfer, wa_xfer_id(xfer), seg->index, - urb->status); - if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)){ - dev_err(dev, "DTO: URB max acceptable errors " - "exceeded, resetting device\n"); - wa_reset_all(wa); - } - seg->result = urb->status; - rpipe_ready = rpipe_avail_inc(rpipe); - if (active_buf_in_urbs == 0) - done = __wa_xfer_mark_seg_as_done(xfer, seg, - WA_SEG_ERROR); - else - __wa_xfer_abort(xfer); - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); - } - - if (resubmit_dti) { - int result; - - wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; - - result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); - if (result < 0) { - dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n", - result); - wa_reset_all(wa); - } - } -} - -/* - * Handle an incoming transfer result buffer - * - * Given a transfer result buffer, it completes the transfer (possibly - * scheduling and buffer in read) and then resubmits the DTI URB for a - * new transfer result read. - * - * - * The xfer_result DTI URB state machine - * - * States: OFF | RXR (Read-Xfer-Result) | RBI (Read-Buffer-In) - * - * We start in OFF mode, the first xfer_result notification [through - * wa_handle_notif_xfer()] moves us to RXR by posting the DTI-URB to - * read. - * - * We receive a buffer -- if it is not a xfer_result, we complain and - * repost the DTI-URB. If it is a xfer_result then do the xfer seg - * request accounting. If it is an IN segment, we move to RBI and post - * a BUF-IN-URB to the right buffer. The BUF-IN-URB callback will - * repost the DTI-URB and move to RXR state. if there was no IN - * segment, it will repost the DTI-URB. - * - * We go back to OFF when we detect a ENOENT or ESHUTDOWN (or too many - * errors) in the URBs. - */ -static void wa_dti_cb(struct urb *urb) -{ - int result, dti_busy = 0; - struct wahc *wa = urb->context; - struct device *dev = &wa->usb_iface->dev; - u32 xfer_id; - u8 usb_status; - - BUG_ON(wa->dti_urb != urb); - switch (wa->dti_urb->status) { - case 0: - if (wa->dti_state == WA_DTI_TRANSFER_RESULT_PENDING) { - struct wa_xfer_result *xfer_result; - struct wa_xfer *xfer; - - /* We have a xfer result buffer; check it */ - dev_dbg(dev, "DTI: xfer result %d bytes at %p\n", - urb->actual_length, urb->transfer_buffer); - if (urb->actual_length != sizeof(*xfer_result)) { - dev_err(dev, "DTI Error: xfer result--bad size xfer result (%d bytes vs %zu needed)\n", - urb->actual_length, - sizeof(*xfer_result)); - break; - } - xfer_result = (struct wa_xfer_result *)(wa->dti_buf); - if (xfer_result->hdr.bLength != sizeof(*xfer_result)) { - dev_err(dev, "DTI Error: xfer result--bad header length %u\n", - xfer_result->hdr.bLength); - break; - } - if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) { - dev_err(dev, "DTI Error: xfer result--bad header type 0x%02x\n", - xfer_result->hdr.bNotifyType); - break; - } - xfer_id = le32_to_cpu(xfer_result->dwTransferID); - usb_status = xfer_result->bTransferStatus & 0x3f; - if (usb_status == WA_XFER_STATUS_NOT_FOUND) { - /* taken care of already */ - dev_dbg(dev, "%s: xfer 0x%08X#%u not found.\n", - __func__, xfer_id, - xfer_result->bTransferSegment & 0x7f); - break; - } - xfer = wa_xfer_get_by_id(wa, xfer_id); - if (xfer == NULL) { - /* FIXME: transaction not found. */ - dev_err(dev, "DTI Error: xfer result--unknown xfer 0x%08x (status 0x%02x)\n", - xfer_id, usb_status); - break; - } - wa_xfer_result_chew(wa, xfer, xfer_result); - wa_xfer_put(xfer); - } else if (wa->dti_state == WA_DTI_ISOC_PACKET_STATUS_PENDING) { - dti_busy = wa_process_iso_packet_status(wa, urb); - } else { - dev_err(dev, "DTI Error: unexpected EP state = %d\n", - wa->dti_state); - } - break; - case -ENOENT: /* (we killed the URB)...so, no broadcast */ - case -ESHUTDOWN: /* going away! */ - dev_dbg(dev, "DTI: going down! %d\n", urb->status); - goto out; - default: - /* Unknown error */ - if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, - EDC_ERROR_TIMEFRAME)) { - dev_err(dev, "DTI: URB max acceptable errors " - "exceeded, resetting device\n"); - wa_reset_all(wa); - goto out; - } - if (printk_ratelimit()) - dev_err(dev, "DTI: URB error %d\n", urb->status); - break; - } - - /* Resubmit the DTI URB if we are not busy processing isoc in frames. */ - if (!dti_busy) { - result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); - if (result < 0) { - dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n", - result); - wa_reset_all(wa); - } - } -out: - return; -} - -/* - * Initialize the DTI URB for reading transfer result notifications and also - * the buffer-in URB, for reading buffers. Then we just submit the DTI URB. - */ -int wa_dti_start(struct wahc *wa) -{ - const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; - struct device *dev = &wa->usb_iface->dev; - int result = -ENOMEM, index; - - if (wa->dti_urb != NULL) /* DTI URB already started */ - goto out; - - wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL); - if (wa->dti_urb == NULL) - goto error_dti_urb_alloc; - usb_fill_bulk_urb( - wa->dti_urb, wa->usb_dev, - usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress), - wa->dti_buf, wa->dti_buf_size, - wa_dti_cb, wa); - - /* init the buf in URBs */ - for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index) { - usb_fill_bulk_urb( - &(wa->buf_in_urbs[index]), wa->usb_dev, - usb_rcvbulkpipe(wa->usb_dev, - 0x80 | dti_epd->bEndpointAddress), - NULL, 0, wa_buf_in_cb, wa); - } - result = usb_submit_urb(wa->dti_urb, GFP_KERNEL); - if (result < 0) { - dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n", - result); - goto error_dti_urb_submit; - } -out: - return 0; - -error_dti_urb_submit: - usb_put_urb(wa->dti_urb); - wa->dti_urb = NULL; -error_dti_urb_alloc: - return result; -} -EXPORT_SYMBOL_GPL(wa_dti_start); -/* - * Transfer complete notification - * - * Called from the notif.c code. We get a notification on EP2 saying - * that some endpoint has some transfer result data available. We are - * about to read it. - * - * To speed up things, we always have a URB reading the DTI URB; we - * don't really set it up and start it until the first xfer complete - * notification arrives, which is what we do here. - * - * Follow up in wa_dti_cb(), as that's where the whole state - * machine starts. - * - * @wa shall be referenced - */ -void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) -{ - struct device *dev = &wa->usb_iface->dev; - struct wa_notif_xfer *notif_xfer; - const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; - - notif_xfer = container_of(notif_hdr, struct wa_notif_xfer, hdr); - BUG_ON(notif_hdr->bNotifyType != WA_NOTIF_TRANSFER); - - if ((0x80 | notif_xfer->bEndpoint) != dti_epd->bEndpointAddress) { - /* FIXME: hardcoded limitation, adapt */ - dev_err(dev, "BUG: DTI ep is %u, not %u (hack me)\n", - notif_xfer->bEndpoint, dti_epd->bEndpointAddress); - goto error; - } - - /* attempt to start the DTI ep processing. */ - if (wa_dti_start(wa) < 0) - goto error; - - return; - -error: - wa_reset_all(wa); -} diff --git a/drivers/staging/wusbcore/wusbhc.c b/drivers/staging/wusbcore/wusbhc.c deleted file mode 100644 index d0b404d258e8..000000000000 --- a/drivers/staging/wusbcore/wusbhc.c +++ /dev/null @@ -1,490 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB Host Controller - * sysfs glue, wusbcore module support and life cycle management - * - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * Creation/destruction of wusbhc is split in two parts; that that - * doesn't require the HCD to be added (wusbhc_{create,destroy}) and - * the one that requires (phase B, wusbhc_b_{create,destroy}). - * - * This is so because usb_add_hcd() will start the HC, and thus, all - * the HC specific stuff has to be already initialized (like sysfs - * thingies). - */ -#include -#include -#include "wusbhc.h" - -/** - * Extract the wusbhc that corresponds to a USB Host Controller class device - * - * WARNING! Apply only if @dev is that of a - * wusbhc.usb_hcd.self->class_dev; otherwise, you loose. - */ -static struct wusbhc *usbhc_dev_to_wusbhc(struct device *dev) -{ - struct usb_bus *usb_bus = dev_get_drvdata(dev); - struct usb_hcd *usb_hcd = bus_to_hcd(usb_bus); - return usb_hcd_to_wusbhc(usb_hcd); -} - -/* - * Show & store the current WUSB trust timeout - * - * We don't do locking--it is an 'atomic' value. - * - * The units that we store/show are always MILLISECONDS. However, the - * value of trust_timeout is jiffies. - */ -static ssize_t wusb_trust_timeout_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - - return scnprintf(buf, PAGE_SIZE, "%u\n", wusbhc->trust_timeout); -} - -static ssize_t wusb_trust_timeout_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - ssize_t result = -ENOSYS; - unsigned trust_timeout; - - result = sscanf(buf, "%u", &trust_timeout); - if (result != 1) { - result = -EINVAL; - goto out; - } - wusbhc->trust_timeout = min_t(unsigned, trust_timeout, 500); - cancel_delayed_work(&wusbhc->keep_alive_timer); - flush_workqueue(wusbd); - queue_delayed_work(wusbd, &wusbhc->keep_alive_timer, - msecs_to_jiffies(wusbhc->trust_timeout / 2)); -out: - return result < 0 ? result : size; -} -static DEVICE_ATTR_RW(wusb_trust_timeout); - -/* - * Show the current WUSB CHID. - */ -static ssize_t wusb_chid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - const struct wusb_ckhdid *chid; - - if (wusbhc->wuie_host_info != NULL) - chid = &wusbhc->wuie_host_info->CHID; - else - chid = &wusb_ckhdid_zero; - - return sprintf(buf, "%16ph\n", chid->data); -} - -/* - * Store a new CHID. - * - * - Write an all zeros CHID and it will stop the controller - * - Write a non-zero CHID and it will start it. - * - * See wusbhc_chid_set() for more info. - */ -static ssize_t wusb_chid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - struct wusb_ckhdid chid; - ssize_t result; - - result = sscanf(buf, - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx " - "%02hhx %02hhx %02hhx %02hhx\n", - &chid.data[0] , &chid.data[1] , - &chid.data[2] , &chid.data[3] , - &chid.data[4] , &chid.data[5] , - &chid.data[6] , &chid.data[7] , - &chid.data[8] , &chid.data[9] , - &chid.data[10], &chid.data[11], - &chid.data[12], &chid.data[13], - &chid.data[14], &chid.data[15]); - if (result != 16) { - dev_err(dev, "Unrecognized CHID (need 16 8-bit hex digits): " - "%d\n", (int)result); - return -EINVAL; - } - result = wusbhc_chid_set(wusbhc, &chid); - return result < 0 ? result : size; -} -static DEVICE_ATTR_RW(wusb_chid); - - -static ssize_t wusb_phy_rate_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - - return sprintf(buf, "%d\n", wusbhc->phy_rate); -} - -static ssize_t wusb_phy_rate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - uint8_t phy_rate; - ssize_t result; - - result = sscanf(buf, "%hhu", &phy_rate); - if (result != 1) - return -EINVAL; - if (phy_rate >= UWB_PHY_RATE_INVALID) - return -EINVAL; - - wusbhc->phy_rate = phy_rate; - return size; -} -static DEVICE_ATTR_RW(wusb_phy_rate); - -static ssize_t wusb_dnts_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - - return sprintf(buf, "num slots: %d\ninterval: %dms\n", - wusbhc->dnts_num_slots, wusbhc->dnts_interval); -} - -static ssize_t wusb_dnts_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - uint8_t num_slots, interval; - ssize_t result; - - result = sscanf(buf, "%hhu %hhu", &num_slots, &interval); - - if (result != 2) - return -EINVAL; - - wusbhc->dnts_num_slots = num_slots; - wusbhc->dnts_interval = interval; - - return size; -} -static DEVICE_ATTR_RW(wusb_dnts); - -static ssize_t wusb_retry_count_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - - return sprintf(buf, "%d\n", wusbhc->retry_count); -} - -static ssize_t wusb_retry_count_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); - uint8_t retry_count; - ssize_t result; - - result = sscanf(buf, "%hhu", &retry_count); - - if (result != 1) - return -EINVAL; - - wusbhc->retry_count = max_t(uint8_t, retry_count, - WUSB_RETRY_COUNT_MAX); - - return size; -} -static DEVICE_ATTR_RW(wusb_retry_count); - -/* Group all the WUSBHC attributes */ -static struct attribute *wusbhc_attrs[] = { - &dev_attr_wusb_trust_timeout.attr, - &dev_attr_wusb_chid.attr, - &dev_attr_wusb_phy_rate.attr, - &dev_attr_wusb_dnts.attr, - &dev_attr_wusb_retry_count.attr, - NULL, -}; - -static const struct attribute_group wusbhc_attr_group = { - .name = NULL, /* we want them in the same directory */ - .attrs = wusbhc_attrs, -}; - -/* - * Create a wusbhc instance - * - * NOTEs: - * - * - assumes *wusbhc has been zeroed and wusbhc->usb_hcd has been - * initialized but not added. - * - * - fill out ports_max, mmcies_max and mmcie_{add,rm} before calling. - * - * - fill out wusbhc->uwb_rc and refcount it before calling - * - fill out the wusbhc->sec_modes array - */ -int wusbhc_create(struct wusbhc *wusbhc) -{ - int result = 0; - - /* set defaults. These can be overwritten using sysfs attributes. */ - wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS; - wusbhc->phy_rate = UWB_PHY_RATE_INVALID - 1; - wusbhc->dnts_num_slots = 4; - wusbhc->dnts_interval = 2; - wusbhc->retry_count = WUSB_RETRY_COUNT_INFINITE; - - mutex_init(&wusbhc->mutex); - result = wusbhc_mmcie_create(wusbhc); - if (result < 0) - goto error_mmcie_create; - result = wusbhc_devconnect_create(wusbhc); - if (result < 0) - goto error_devconnect_create; - result = wusbhc_rh_create(wusbhc); - if (result < 0) - goto error_rh_create; - result = wusbhc_sec_create(wusbhc); - if (result < 0) - goto error_sec_create; - return 0; - -error_sec_create: - wusbhc_rh_destroy(wusbhc); -error_rh_create: - wusbhc_devconnect_destroy(wusbhc); -error_devconnect_create: - wusbhc_mmcie_destroy(wusbhc); -error_mmcie_create: - return result; -} -EXPORT_SYMBOL_GPL(wusbhc_create); - -static inline struct kobject *wusbhc_kobj(struct wusbhc *wusbhc) -{ - return &wusbhc->usb_hcd.self.controller->kobj; -} - -/* - * Phase B of a wusbhc instance creation - * - * Creates fields that depend on wusbhc->usb_hcd having been - * added. This is where we create the sysfs files in - * /sys/class/usb_host/usb_hostX/. - * - * NOTE: Assumes wusbhc->usb_hcd has been already added by the upper - * layer (hwahc or whci) - */ -int wusbhc_b_create(struct wusbhc *wusbhc) -{ - int result = 0; - struct device *dev = wusbhc->usb_hcd.self.controller; - - result = sysfs_create_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); - if (result < 0) { - dev_err(dev, "Cannot register WUSBHC attributes: %d\n", - result); - goto error_create_attr_group; - } - - return 0; -error_create_attr_group: - return result; -} -EXPORT_SYMBOL_GPL(wusbhc_b_create); - -void wusbhc_b_destroy(struct wusbhc *wusbhc) -{ - wusbhc_pal_unregister(wusbhc); - sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); -} -EXPORT_SYMBOL_GPL(wusbhc_b_destroy); - -void wusbhc_destroy(struct wusbhc *wusbhc) -{ - wusbhc_sec_destroy(wusbhc); - wusbhc_rh_destroy(wusbhc); - wusbhc_devconnect_destroy(wusbhc); - wusbhc_mmcie_destroy(wusbhc); -} -EXPORT_SYMBOL_GPL(wusbhc_destroy); - -struct workqueue_struct *wusbd; -EXPORT_SYMBOL_GPL(wusbd); - -/* - * WUSB Cluster ID allocation map - * - * Each WUSB bus in a channel is identified with a Cluster Id in the - * unauth address pace (WUSB1.0[4.3]). We take the range 0xe0 to 0xff - * (that's space for 31 WUSB controllers, as 0xff can't be taken). We - * start taking from 0xff, 0xfe, 0xfd... (hence the += or -= 0xff). - * - * For each one we taken, we pin it in the bitap - */ -#define CLUSTER_IDS 32 -static DECLARE_BITMAP(wusb_cluster_id_table, CLUSTER_IDS); -static DEFINE_SPINLOCK(wusb_cluster_ids_lock); - -/* - * Get a WUSB Cluster ID - * - * Need to release with wusb_cluster_id_put() when done w/ it. - */ -/* FIXME: coordinate with the choose_addres() from the USB stack */ -/* we want to leave the top of the 128 range for cluster addresses and - * the bottom for device addresses (as we map them one on one with - * ports). */ -u8 wusb_cluster_id_get(void) -{ - u8 id; - spin_lock(&wusb_cluster_ids_lock); - id = find_first_zero_bit(wusb_cluster_id_table, CLUSTER_IDS); - if (id >= CLUSTER_IDS) { - id = 0; - goto out; - } - set_bit(id, wusb_cluster_id_table); - id = (u8) 0xff - id; -out: - spin_unlock(&wusb_cluster_ids_lock); - return id; - -} -EXPORT_SYMBOL_GPL(wusb_cluster_id_get); - -/* - * Release a WUSB Cluster ID - * - * Obtained it with wusb_cluster_id_get() - */ -void wusb_cluster_id_put(u8 id) -{ - id = 0xff - id; - BUG_ON(id >= CLUSTER_IDS); - spin_lock(&wusb_cluster_ids_lock); - WARN_ON(!test_bit(id, wusb_cluster_id_table)); - clear_bit(id, wusb_cluster_id_table); - spin_unlock(&wusb_cluster_ids_lock); -} -EXPORT_SYMBOL_GPL(wusb_cluster_id_put); - -/** - * wusbhc_giveback_urb - return an URB to the USB core - * @wusbhc: the host controller the URB is from. - * @urb: the URB. - * @status: the URB's status. - * - * Return an URB to the USB core doing some additional WUSB specific - * processing. - * - * - After a successful transfer, update the trust timeout timestamp - * for the WUSB device. - * - * - [WUSB] sections 4.13 and 7.5.1 specify the stop retransmission - * condition for the WCONNECTACK_IE is that the host has observed - * the associated device responding to a control transfer. - */ -void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, int status) -{ - struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, - urb->dev); - - if (status == 0 && wusb_dev) { - wusb_dev->entry_ts = jiffies; - - /* wusbhc_devconnect_acked() can't be called from - atomic context so defer it to a work queue. */ - if (!list_empty(&wusb_dev->cack_node)) - queue_work(wusbd, &wusb_dev->devconnect_acked_work); - else - wusb_dev_put(wusb_dev); - } - - usb_hcd_giveback_urb(&wusbhc->usb_hcd, urb, status); -} -EXPORT_SYMBOL_GPL(wusbhc_giveback_urb); - -/** - * wusbhc_reset_all - reset the HC hardware - * @wusbhc: the host controller to reset. - * - * Request a full hardware reset of the chip. This will also reset - * the radio controller and any other PALs. - */ -void wusbhc_reset_all(struct wusbhc *wusbhc) -{ - if (wusbhc->uwb_rc) - uwb_rc_reset_all(wusbhc->uwb_rc); -} -EXPORT_SYMBOL_GPL(wusbhc_reset_all); - -static struct notifier_block wusb_usb_notifier = { - .notifier_call = wusb_usb_ncb, - .priority = INT_MAX /* Need to be called first of all */ -}; - -static int __init wusbcore_init(void) -{ - int result; - result = wusb_crypto_init(); - if (result < 0) - goto error_crypto_init; - /* WQ is singlethread because we need to serialize notifications */ - wusbd = create_singlethread_workqueue("wusbd"); - if (wusbd == NULL) { - result = -ENOMEM; - printk(KERN_ERR "WUSB-core: Cannot create wusbd workqueue\n"); - goto error_wusbd_create; - } - usb_register_notify(&wusb_usb_notifier); - bitmap_zero(wusb_cluster_id_table, CLUSTER_IDS); - set_bit(0, wusb_cluster_id_table); /* reserve Cluster ID 0xff */ - return 0; - -error_wusbd_create: - wusb_crypto_exit(); -error_crypto_init: - return result; - -} -module_init(wusbcore_init); - -static void __exit wusbcore_exit(void) -{ - clear_bit(0, wusb_cluster_id_table); - if (!bitmap_empty(wusb_cluster_id_table, CLUSTER_IDS)) { - printk(KERN_ERR "BUG: WUSB Cluster IDs not released on exit: %*pb\n", - CLUSTER_IDS, wusb_cluster_id_table); - WARN_ON(1); - } - usb_unregister_notify(&wusb_usb_notifier); - destroy_workqueue(wusbd); - wusb_crypto_exit(); -} -module_exit(wusbcore_exit); - -MODULE_AUTHOR("Inaky Perez-Gonzalez "); -MODULE_DESCRIPTION("Wireless USB core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/wusbcore/wusbhc.h b/drivers/staging/wusbcore/wusbhc.h deleted file mode 100644 index 716244a2ec44..000000000000 --- a/drivers/staging/wusbcore/wusbhc.h +++ /dev/null @@ -1,487 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless USB Host Controller - * Common infrastructure for WHCI and HWA WUSB-HC drivers - * - * - * Copyright (C) 2005-2006 Intel Corporation - * Inaky Perez-Gonzalez - * - * This driver implements parts common to all Wireless USB Host - * Controllers (struct wusbhc, embedding a struct usb_hcd) and is used - * by: - * - * - hwahc: HWA, USB-dongle that implements a Wireless USB host - * controller, (Wireless USB 1.0 Host-Wire-Adapter specification). - * - * - whci: WHCI, a PCI card with a wireless host controller - * (Wireless Host Controller Interface 1.0 specification). - * - * Check out the Design-overview.txt file in the source documentation - * for other details on the implementation. - * - * Main blocks: - * - * rh Root Hub emulation (part of the HCD glue) - * - * devconnect Handle all the issues related to device connection, - * authentication, disconnection, timeout, reseting, - * keepalives, etc. - * - * mmc MMC IE broadcasting handling - * - * A host controller driver just initializes its stuff and as part of - * that, creates a 'struct wusbhc' instance that handles all the - * common WUSB mechanisms. Links in the function ops that are specific - * to it and then registers the host controller. Ready to run. - */ - -#ifndef __WUSBHC_H__ -#define __WUSBHC_H__ - -#include -#include -#include -#include -#include -#include -#include "../uwb/uwb.h" -#include "include/wusb.h" - -/* - * Time from a WUSB channel stop request to the last transmitted MMC. - * - * This needs to be > 4.096 ms in case no MMCs can be transmitted in - * zone 0. - */ -#define WUSB_CHANNEL_STOP_DELAY_MS 8 -#define WUSB_RETRY_COUNT_MAX 15 -#define WUSB_RETRY_COUNT_INFINITE 0 - -/** - * Wireless USB device - * - * Describe a WUSB device connected to the cluster. This struct - * belongs to the 'struct wusb_port' it is attached to and it is - * responsible for putting and clearing the pointer to it. - * - * Note this "complements" the 'struct usb_device' that the usb_hcd - * keeps for each connected USB device. However, it extends some - * information that is not available (there is no hcpriv ptr in it!) - * *and* most importantly, it's life cycle is different. It is created - * as soon as we get a DN_Connect (connect request notification) from - * the device through the WUSB host controller; the USB stack doesn't - * create the device until we authenticate it. FIXME: this will - * change. - * - * @bos: This is allocated when the BOS descriptors are read from - * the device and freed upon the wusb_dev struct dying. - * @wusb_cap_descr: points into @bos, and has been verified to be size - * safe. - */ -struct wusb_dev { - struct kref refcnt; - struct wusbhc *wusbhc; - struct list_head cack_node; /* Connect-Ack list */ - struct list_head rekey_node; /* GTK rekey list */ - u8 port_idx; - u8 addr; - u8 beacon_type:4; - struct usb_encryption_descriptor ccm1_etd; - struct wusb_ckhdid cdid; - unsigned long entry_ts; - struct usb_bos_descriptor *bos; - struct usb_wireless_cap_descriptor *wusb_cap_descr; - struct uwb_mas_bm availability; - struct work_struct devconnect_acked_work; - struct usb_device *usb_dev; -}; - -#define WUSB_DEV_ADDR_UNAUTH 0x80 - -static inline void wusb_dev_init(struct wusb_dev *wusb_dev) -{ - kref_init(&wusb_dev->refcnt); - /* no need to init the cack_node */ -} - -extern void wusb_dev_destroy(struct kref *_wusb_dev); - -static inline struct wusb_dev *wusb_dev_get(struct wusb_dev *wusb_dev) -{ - kref_get(&wusb_dev->refcnt); - return wusb_dev; -} - -static inline void wusb_dev_put(struct wusb_dev *wusb_dev) -{ - kref_put(&wusb_dev->refcnt, wusb_dev_destroy); -} - -/** - * Wireless USB Host Controller root hub "fake" ports - * (state and device information) - * - * Wireless USB is wireless, so there are no ports; but we - * fake'em. Each RC can connect a max of devices at the same time - * (given in the Wireless Adapter descriptor, bNumPorts or WHCI's - * caps), referred to in wusbhc->ports_max. - * - * See rh.c for more information. - * - * The @status and @change use the same bits as in USB2.0[11.24.2.7], - * so we don't have to do much when getting the port's status. - * - * WUSB1.0[7.1], USB2.0[11.24.2.7.1,fig 11-10], - * include/linux/usb_ch9.h (#define USB_PORT_STAT_*) - */ -struct wusb_port { - u16 status; - u16 change; - struct wusb_dev *wusb_dev; /* connected device's info */ - u32 ptk_tkid; -}; - -/** - * WUSB Host Controller specifics - * - * All fields that are common to all Wireless USB controller types - * (HWA and WHCI) are grouped here. Host Controller - * functions/operations that only deal with general Wireless USB HC - * issues use this data type to refer to the host. - * - * @usb_hcd Instantiation of a USB host controller - * (initialized by upper layer [HWA=HC or WHCI]. - * - * @dev Device that implements this; initialized by the - * upper layer (HWA-HC, WHCI...); this device should - * have a refcount. - * - * @trust_timeout After this time without hearing for device - * activity, we consider the device gone and we have to - * re-authenticate. - * - * Can be accessed w/o locking--however, read to a - * local variable then use. - * - * @chid WUSB Cluster Host ID: this is supposed to be a - * unique value that doesn't change across reboots (so - * that your devices do not require re-association). - * - * Read/Write protected by @mutex - * - * @dev_info This array has ports_max elements. It is used to - * give the HC information about the WUSB devices (see - * 'struct wusb_dev_info'). - * - * For HWA we need to allocate it in heap; for WHCI it - * needs to be permanently mapped, so we keep it for - * both and make it easy. Call wusbhc->dev_info_set() - * to update an entry. - * - * @ports_max Number of simultaneous device connections (fake - * ports) this HC will take. Read-only. - * - * @port Array of port status for each fake root port. Guaranteed to - * always be the same length during device existence - * [this allows for some unlocked but referenced reading]. - * - * @mmcies_max Max number of Information Elements this HC can send - * in its MMC. Read-only. - * - * @start Start the WUSB channel. - * - * @stop Stop the WUSB channel after the specified number of - * milliseconds. Channel Stop IEs should be transmitted - * as required by [WUSB] 4.16.2.1. - * - * @mmcie_add HC specific operation (WHCI or HWA) for adding an - * MMCIE. - * - * @mmcie_rm HC specific operation (WHCI or HWA) for removing an - * MMCIE. - * - * @set_ptk: Set the PTK and enable encryption for a device. Or, if - * the supplied key is NULL, disable encryption for that - * device. - * - * @set_gtk: Set the GTK to be used for all future broadcast packets - * (i.e., MMCs). With some hardware, setting the GTK may start - * MMC transmission. - * - * NOTE: - * - * - If wusb_dev->usb_dev is not NULL, then usb_dev is valid - * (wusb_dev has a refcount on it). Likewise, if usb_dev->wusb_dev - * is not NULL, usb_dev->wusb_dev is valid (usb_dev keeps a - * refcount on it). - * - * Most of the times when you need to use it, it will be non-NULL, - * so there is no real need to check for it (wusb_dev will - * disappear before usb_dev). - * - * - The following fields need to be filled out before calling - * wusbhc_create(): ports_max, mmcies_max, mmcie_{add,rm}. - * - * - there is no wusbhc_init() method, we do everything in - * wusbhc_create(). - * - * - Creation is done in two phases, wusbhc_create() and - * wusbhc_create_b(); b are the parts that need to be called after - * calling usb_hcd_add(&wusbhc->usb_hcd). - */ -struct wusbhc { - struct usb_hcd usb_hcd; /* HAS TO BE 1st */ - struct device *dev; - struct uwb_rc *uwb_rc; - struct uwb_pal pal; - - unsigned trust_timeout; /* in jiffies */ - struct wusb_ckhdid chid; - uint8_t phy_rate; - uint8_t dnts_num_slots; - uint8_t dnts_interval; - uint8_t retry_count; - struct wuie_host_info *wuie_host_info; - - struct mutex mutex; /* locks everything else */ - u16 cluster_id; /* Wireless USB Cluster ID */ - struct wusb_port *port; /* Fake port status handling */ - struct wusb_dev_info *dev_info; /* for Set Device Info mgmt */ - u8 ports_max; - unsigned active:1; /* currently xmit'ing MMCs */ - struct wuie_keep_alive keep_alive_ie; /* protected by mutex */ - struct delayed_work keep_alive_timer; - struct list_head cack_list; /* Connect acknowledging */ - size_t cack_count; /* protected by 'mutex' */ - struct wuie_connect_ack cack_ie; - struct uwb_rsv *rsv; /* cluster bandwidth reservation */ - - struct mutex mmcie_mutex; /* MMC WUIE handling */ - struct wuie_hdr **mmcie; /* WUIE array */ - u8 mmcies_max; - /* FIXME: make wusbhc_ops? */ - int (*start)(struct wusbhc *wusbhc); - void (*stop)(struct wusbhc *wusbhc, int delay); - int (*mmcie_add)(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, - u8 handle, struct wuie_hdr *wuie); - int (*mmcie_rm)(struct wusbhc *wusbhc, u8 handle); - int (*dev_info_set)(struct wusbhc *, struct wusb_dev *wusb_dev); - int (*bwa_set)(struct wusbhc *wusbhc, s8 stream_index, - const struct uwb_mas_bm *); - int (*set_ptk)(struct wusbhc *wusbhc, u8 port_idx, - u32 tkid, const void *key, size_t key_size); - int (*set_gtk)(struct wusbhc *wusbhc, - u32 tkid, const void *key, size_t key_size); - int (*set_num_dnts)(struct wusbhc *wusbhc, u8 interval, u8 slots); - - struct { - struct usb_key_descriptor descr; - u8 data[16]; /* GTK key data */ - } __attribute__((packed)) gtk; - u8 gtk_index; - u32 gtk_tkid; - - /* workqueue for WUSB security related tasks. */ - struct workqueue_struct *wq_security; - struct work_struct gtk_rekey_work; - - struct usb_encryption_descriptor *ccm1_etd; -}; - -#define usb_hcd_to_wusbhc(u) container_of((u), struct wusbhc, usb_hcd) - - -extern int wusbhc_create(struct wusbhc *); -extern int wusbhc_b_create(struct wusbhc *); -extern void wusbhc_b_destroy(struct wusbhc *); -extern void wusbhc_destroy(struct wusbhc *); -extern int wusb_dev_sysfs_add(struct wusbhc *, struct usb_device *, - struct wusb_dev *); -extern void wusb_dev_sysfs_rm(struct wusb_dev *); -extern int wusbhc_sec_create(struct wusbhc *); -extern int wusbhc_sec_start(struct wusbhc *); -extern void wusbhc_sec_stop(struct wusbhc *); -extern void wusbhc_sec_destroy(struct wusbhc *); -extern void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, - int status); -void wusbhc_reset_all(struct wusbhc *wusbhc); - -int wusbhc_pal_register(struct wusbhc *wusbhc); -void wusbhc_pal_unregister(struct wusbhc *wusbhc); - -/* - * Return @usb_dev's @usb_hcd (properly referenced) or NULL if gone - * - * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr) - * - * This is a safe assumption as @usb_dev->bus is referenced all the - * time during the @usb_dev life cycle. - */ -static inline -struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev) -{ - struct usb_hcd *usb_hcd; - usb_hcd = bus_to_hcd(usb_dev->bus); - return usb_get_hcd(usb_hcd); -} - -/* - * Increment the reference count on a wusbhc. - * - * @wusbhc's life cycle is identical to that of the underlying usb_hcd. - */ -static inline struct wusbhc *wusbhc_get(struct wusbhc *wusbhc) -{ - return usb_get_hcd(&wusbhc->usb_hcd) ? wusbhc : NULL; -} - -/* - * Return the wusbhc associated to a @usb_dev - * - * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr) - * - * @returns: wusbhc for @usb_dev; NULL if the @usb_dev is being torn down. - * WARNING: referenced at the usb_hcd level, unlocked - * - * FIXME: move offline - */ -static inline struct wusbhc *wusbhc_get_by_usb_dev(struct usb_device *usb_dev) -{ - struct wusbhc *wusbhc = NULL; - struct usb_hcd *usb_hcd; - if (usb_dev->devnum > 1 && !usb_dev->wusb) { - /* but root hubs */ - dev_err(&usb_dev->dev, "devnum %d wusb %d\n", usb_dev->devnum, - usb_dev->wusb); - BUG_ON(usb_dev->devnum > 1 && !usb_dev->wusb); - } - usb_hcd = usb_hcd_get_by_usb_dev(usb_dev); - if (usb_hcd == NULL) - return NULL; - BUG_ON(usb_hcd->wireless == 0); - return wusbhc = usb_hcd_to_wusbhc(usb_hcd); -} - - -static inline void wusbhc_put(struct wusbhc *wusbhc) -{ - usb_put_hcd(&wusbhc->usb_hcd); -} - -int wusbhc_start(struct wusbhc *wusbhc); -void wusbhc_stop(struct wusbhc *wusbhc); -extern int wusbhc_chid_set(struct wusbhc *, const struct wusb_ckhdid *); - -/* Device connect handling */ -extern int wusbhc_devconnect_create(struct wusbhc *); -extern void wusbhc_devconnect_destroy(struct wusbhc *); -extern int wusbhc_devconnect_start(struct wusbhc *wusbhc); -extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc); -extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr, - struct wusb_dn_hdr *dn_hdr, size_t size); -extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port); -extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val, - void *priv); -extern int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, - u8 addr); - -/* Wireless USB fake Root Hub methods */ -extern int wusbhc_rh_create(struct wusbhc *); -extern void wusbhc_rh_destroy(struct wusbhc *); - -extern int wusbhc_rh_status_data(struct usb_hcd *, char *); -extern int wusbhc_rh_control(struct usb_hcd *, u16, u16, u16, char *, u16); -extern int wusbhc_rh_start_port_reset(struct usb_hcd *, unsigned); - -/* MMC handling */ -extern int wusbhc_mmcie_create(struct wusbhc *); -extern void wusbhc_mmcie_destroy(struct wusbhc *); -extern int wusbhc_mmcie_set(struct wusbhc *, u8 interval, u8 repeat_cnt, - struct wuie_hdr *); -extern void wusbhc_mmcie_rm(struct wusbhc *, struct wuie_hdr *); - -/* Bandwidth reservation */ -int wusbhc_rsv_establish(struct wusbhc *wusbhc); -void wusbhc_rsv_terminate(struct wusbhc *wusbhc); - -/* - * I've always said - * I wanted a wedding in a church... - * - * but lately I've been thinking about - * the Botanical Gardens. - * - * We could do it by the tulips. - * It'll be beautiful - * - * --Security! - */ -extern int wusb_dev_sec_add(struct wusbhc *, struct usb_device *, - struct wusb_dev *); -extern void wusb_dev_sec_rm(struct wusb_dev *) ; -extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *, - struct wusb_ckhdid *ck); -void wusbhc_gtk_rekey(struct wusbhc *wusbhc); -int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev); - - -/* WUSB Cluster ID handling */ -extern u8 wusb_cluster_id_get(void); -extern void wusb_cluster_id_put(u8); - -/* - * wusb_port_by_idx - return the port associated to a zero-based port index - * - * NOTE: valid without locking as long as wusbhc is referenced (as the - * number of ports doesn't change). The data pointed to has to - * be verified though :) - */ -static inline struct wusb_port *wusb_port_by_idx(struct wusbhc *wusbhc, - u8 port_idx) -{ - return &wusbhc->port[port_idx]; -} - -/* - * wusb_port_no_to_idx - Convert port number (per usb_dev->portnum) to - * a port_idx. - * - * USB stack USB ports are 1 based!! - * - * NOTE: only valid for WUSB devices!!! - */ -static inline u8 wusb_port_no_to_idx(u8 port_no) -{ - return port_no - 1; -} - -extern struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *, - struct usb_device *); - -/* - * Return a referenced wusb_dev given a @usb_dev - * - * Returns NULL if the usb_dev is being torn down. - * - * FIXME: move offline - */ -static inline -struct wusb_dev *wusb_dev_get_by_usb_dev(struct usb_device *usb_dev) -{ - struct wusbhc *wusbhc; - struct wusb_dev *wusb_dev; - wusbhc = wusbhc_get_by_usb_dev(usb_dev); - if (wusbhc == NULL) - return NULL; - mutex_lock(&wusbhc->mutex); - wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev); - mutex_unlock(&wusbhc->mutex); - wusbhc_put(wusbhc); - return wusb_dev; -} - -/* Misc */ - -extern struct workqueue_struct *wusbd; -#endif /* #ifndef __WUSBHC_H__ */ From 96b06c0a16f737e9ea7dff1e23dd5f6d847e6731 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 5 Feb 2020 13:11:11 +1300 Subject: [PATCH 0154/2231] Revert "staging: octeon-usb: delete the octeon usb host controller driver" This reverts commit 95ace52e4036482da1895b6e19f15141802cc3dd. Re-instate the code so subsequent commits can clean it up and get it building properly. Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20200205001116.14096-2-chris.packham@alliedtelesis.co.nz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/octeon-usb/Kconfig | 11 + drivers/staging/octeon-usb/Makefile | 2 + drivers/staging/octeon-usb/TODO | 8 + drivers/staging/octeon-usb/octeon-hcd.c | 3737 +++++++++++++++++++++++ drivers/staging/octeon-usb/octeon-hcd.h | 1847 +++++++++++ 7 files changed, 5608 insertions(+) create mode 100644 drivers/staging/octeon-usb/Kconfig create mode 100644 drivers/staging/octeon-usb/Makefile create mode 100644 drivers/staging/octeon-usb/TODO create mode 100644 drivers/staging/octeon-usb/octeon-hcd.c create mode 100644 drivers/staging/octeon-usb/octeon-hcd.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 0f82e23e151b..9ec982d16e1c 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -42,6 +42,8 @@ source "drivers/staging/rtl8188eu/Kconfig" source "drivers/staging/rts5208/Kconfig" +source "drivers/staging/octeon-usb/Kconfig" + source "drivers/staging/vt6655/Kconfig" source "drivers/staging/vt6656/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 49b21951b6f2..be3f0a814bf2 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_R8712U) += rtl8712/ obj-$(CONFIG_R8188EU) += rtl8188eu/ obj-$(CONFIG_RTS5208) += rts5208/ obj-$(CONFIG_NETLOGIC_XLR_NET) += netlogic/ +obj-$(CONFIG_OCTEON_USB) += octeon-usb/ obj-$(CONFIG_VT6655) += vt6655/ obj-$(CONFIG_VT6656) += vt6656/ obj-$(CONFIG_VME_BUS) += vme/ diff --git a/drivers/staging/octeon-usb/Kconfig b/drivers/staging/octeon-usb/Kconfig new file mode 100644 index 000000000000..6a5d842ee0f2 --- /dev/null +++ b/drivers/staging/octeon-usb/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +config OCTEON_USB + tristate "Cavium Networks Octeon USB support" + depends on CAVIUM_OCTEON_SOC && USB + help + This driver supports USB host controller on some Cavium + Networks' products in the Octeon family. + + To compile this driver as a module, choose M here. The module + will be called octeon-hcd. + diff --git a/drivers/staging/octeon-usb/Makefile b/drivers/staging/octeon-usb/Makefile new file mode 100644 index 000000000000..9873a0130ad5 --- /dev/null +++ b/drivers/staging/octeon-usb/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-${CONFIG_OCTEON_USB} := octeon-hcd.o diff --git a/drivers/staging/octeon-usb/TODO b/drivers/staging/octeon-usb/TODO new file mode 100644 index 000000000000..2b29acca5caa --- /dev/null +++ b/drivers/staging/octeon-usb/TODO @@ -0,0 +1,8 @@ +This driver is functional and has been tested on EdgeRouter Lite, +D-Link DSR-1000N and EBH5600 evaluation board with USB mass storage. + +TODO: + - kernel coding style + - checkpatch warnings + +Contact: Aaro Koskinen diff --git a/drivers/staging/octeon-usb/octeon-hcd.c b/drivers/staging/octeon-usb/octeon-hcd.c new file mode 100644 index 000000000000..582c9187559d --- /dev/null +++ b/drivers/staging/octeon-usb/octeon-hcd.c @@ -0,0 +1,3737 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 Cavium Networks + * + * Some parts of the code were originally released under BSD license: + * + * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of Cavium Networks nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * This Software, including technical data, may be subject to U.S. export + * control laws, including the U.S. Export Administration Act and its associated + * regulations, and may be subject to export or import regulations in other + * countries. + * + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" + * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR + * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO + * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION + * OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM + * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, + * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF + * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR + * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR + * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "octeon-hcd.h" + +/** + * enum cvmx_usb_speed - the possible USB device speeds + * + * @CVMX_USB_SPEED_HIGH: Device is operation at 480Mbps + * @CVMX_USB_SPEED_FULL: Device is operation at 12Mbps + * @CVMX_USB_SPEED_LOW: Device is operation at 1.5Mbps + */ +enum cvmx_usb_speed { + CVMX_USB_SPEED_HIGH = 0, + CVMX_USB_SPEED_FULL = 1, + CVMX_USB_SPEED_LOW = 2, +}; + +/** + * enum cvmx_usb_transfer - the possible USB transfer types + * + * @CVMX_USB_TRANSFER_CONTROL: USB transfer type control for hub and status + * transfers + * @CVMX_USB_TRANSFER_ISOCHRONOUS: USB transfer type isochronous for low + * priority periodic transfers + * @CVMX_USB_TRANSFER_BULK: USB transfer type bulk for large low priority + * transfers + * @CVMX_USB_TRANSFER_INTERRUPT: USB transfer type interrupt for high priority + * periodic transfers + */ +enum cvmx_usb_transfer { + CVMX_USB_TRANSFER_CONTROL = 0, + CVMX_USB_TRANSFER_ISOCHRONOUS = 1, + CVMX_USB_TRANSFER_BULK = 2, + CVMX_USB_TRANSFER_INTERRUPT = 3, +}; + +/** + * enum cvmx_usb_direction - the transfer directions + * + * @CVMX_USB_DIRECTION_OUT: Data is transferring from Octeon to the device/host + * @CVMX_USB_DIRECTION_IN: Data is transferring from the device/host to Octeon + */ +enum cvmx_usb_direction { + CVMX_USB_DIRECTION_OUT, + CVMX_USB_DIRECTION_IN, +}; + +/** + * enum cvmx_usb_status - possible callback function status codes + * + * @CVMX_USB_STATUS_OK: The transaction / operation finished without + * any errors + * @CVMX_USB_STATUS_SHORT: FIXME: This is currently not implemented + * @CVMX_USB_STATUS_CANCEL: The transaction was canceled while in flight + * by a user call to cvmx_usb_cancel + * @CVMX_USB_STATUS_ERROR: The transaction aborted with an unexpected + * error status + * @CVMX_USB_STATUS_STALL: The transaction received a USB STALL response + * from the device + * @CVMX_USB_STATUS_XACTERR: The transaction failed with an error from the + * device even after a number of retries + * @CVMX_USB_STATUS_DATATGLERR: The transaction failed with a data toggle + * error even after a number of retries + * @CVMX_USB_STATUS_BABBLEERR: The transaction failed with a babble error + * @CVMX_USB_STATUS_FRAMEERR: The transaction failed with a frame error + * even after a number of retries + */ +enum cvmx_usb_status { + CVMX_USB_STATUS_OK, + CVMX_USB_STATUS_SHORT, + CVMX_USB_STATUS_CANCEL, + CVMX_USB_STATUS_ERROR, + CVMX_USB_STATUS_STALL, + CVMX_USB_STATUS_XACTERR, + CVMX_USB_STATUS_DATATGLERR, + CVMX_USB_STATUS_BABBLEERR, + CVMX_USB_STATUS_FRAMEERR, +}; + +/** + * struct cvmx_usb_port_status - the USB port status information + * + * @port_enabled: 1 = Usb port is enabled, 0 = disabled + * @port_over_current: 1 = Over current detected, 0 = Over current not + * detected. Octeon doesn't support over current detection. + * @port_powered: 1 = Port power is being supplied to the device, 0 = + * power is off. Octeon doesn't support turning port power + * off. + * @port_speed: Current port speed. + * @connected: 1 = A device is connected to the port, 0 = No device is + * connected. + * @connect_change: 1 = Device connected state changed since the last set + * status call. + */ +struct cvmx_usb_port_status { + u32 reserved : 25; + u32 port_enabled : 1; + u32 port_over_current : 1; + u32 port_powered : 1; + enum cvmx_usb_speed port_speed : 2; + u32 connected : 1; + u32 connect_change : 1; +}; + +/** + * struct cvmx_usb_iso_packet - descriptor for Isochronous packets + * + * @offset: This is the offset in bytes into the main buffer where this data + * is stored. + * @length: This is the length in bytes of the data. + * @status: This is the status of this individual packet transfer. + */ +struct cvmx_usb_iso_packet { + int offset; + int length; + enum cvmx_usb_status status; +}; + +/** + * enum cvmx_usb_initialize_flags - flags used by the initialization function + * + * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI: The USB port uses a 12MHz crystal + * as clock source at USB_XO and + * USB_XI. + * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND: The USB port uses 12/24/48MHz 2.5V + * board clock source at USB_XO. + * USB_XI should be tied to GND. + * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK: Mask for clock speed field + * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ: Speed of reference clock or + * crystal + * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ: Speed of reference clock + * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ: Speed of reference clock + * @CVMX_USB_INITIALIZE_FLAGS_NO_DMA: Disable DMA and used polled IO for + * data transfer use for the USB + */ +enum cvmx_usb_initialize_flags { + CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI = 1 << 0, + CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND = 1 << 1, + CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK = 3 << 3, + CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ = 1 << 3, + CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ = 2 << 3, + CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ = 3 << 3, + /* Bits 3-4 used to encode the clock frequency */ + CVMX_USB_INITIALIZE_FLAGS_NO_DMA = 1 << 5, +}; + +/** + * enum cvmx_usb_pipe_flags - internal flags for a pipe. + * + * @CVMX_USB_PIPE_FLAGS_SCHEDULED: Used internally to determine if a pipe is + * actively using hardware. + * @CVMX_USB_PIPE_FLAGS_NEED_PING: Used internally to determine if a high speed + * pipe is in the ping state. + */ +enum cvmx_usb_pipe_flags { + CVMX_USB_PIPE_FLAGS_SCHEDULED = 1 << 17, + CVMX_USB_PIPE_FLAGS_NEED_PING = 1 << 18, +}; + +/* Maximum number of times to retry failed transactions */ +#define MAX_RETRIES 3 + +/* Maximum number of hardware channels supported by the USB block */ +#define MAX_CHANNELS 8 + +/* + * The low level hardware can transfer a maximum of this number of bytes in each + * transfer. The field is 19 bits wide + */ +#define MAX_TRANSFER_BYTES ((1 << 19) - 1) + +/* + * The low level hardware can transfer a maximum of this number of packets in + * each transfer. The field is 10 bits wide + */ +#define MAX_TRANSFER_PACKETS ((1 << 10) - 1) + +/** + * Logical transactions may take numerous low level + * transactions, especially when splits are concerned. This + * enum represents all of the possible stages a transaction can + * be in. Note that split completes are always even. This is so + * the NAK handler can backup to the previous low level + * transaction with a simple clearing of bit 0. + */ +enum cvmx_usb_stage { + CVMX_USB_STAGE_NON_CONTROL, + CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE, + CVMX_USB_STAGE_SETUP, + CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE, + CVMX_USB_STAGE_DATA, + CVMX_USB_STAGE_DATA_SPLIT_COMPLETE, + CVMX_USB_STAGE_STATUS, + CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE, +}; + +/** + * struct cvmx_usb_transaction - describes each pending USB transaction + * regardless of type. These are linked together + * to form a list of pending requests for a pipe. + * + * @node: List node for transactions in the pipe. + * @type: Type of transaction, duplicated of the pipe. + * @flags: State flags for this transaction. + * @buffer: User's physical buffer address to read/write. + * @buffer_length: Size of the user's buffer in bytes. + * @control_header: For control transactions, physical address of the 8 + * byte standard header. + * @iso_start_frame: For ISO transactions, the starting frame number. + * @iso_number_packets: For ISO transactions, the number of packets in the + * request. + * @iso_packets: For ISO transactions, the sub packets in the request. + * @actual_bytes: Actual bytes transfer for this transaction. + * @stage: For control transactions, the current stage. + * @urb: URB. + */ +struct cvmx_usb_transaction { + struct list_head node; + enum cvmx_usb_transfer type; + u64 buffer; + int buffer_length; + u64 control_header; + int iso_start_frame; + int iso_number_packets; + struct cvmx_usb_iso_packet *iso_packets; + int xfersize; + int pktcnt; + int retries; + int actual_bytes; + enum cvmx_usb_stage stage; + struct urb *urb; +}; + +/** + * struct cvmx_usb_pipe - a pipe represents a virtual connection between Octeon + * and some USB device. It contains a list of pending + * request to the device. + * + * @node: List node for pipe list + * @next: Pipe after this one in the list + * @transactions: List of pending transactions + * @interval: For periodic pipes, the interval between packets in + * frames + * @next_tx_frame: The next frame this pipe is allowed to transmit on + * @flags: State flags for this pipe + * @device_speed: Speed of device connected to this pipe + * @transfer_type: Type of transaction supported by this pipe + * @transfer_dir: IN or OUT. Ignored for Control + * @multi_count: Max packet in a row for the device + * @max_packet: The device's maximum packet size in bytes + * @device_addr: USB device address at other end of pipe + * @endpoint_num: USB endpoint number at other end of pipe + * @hub_device_addr: Hub address this device is connected to + * @hub_port: Hub port this device is connected to + * @pid_toggle: This toggles between 0/1 on every packet send to track + * the data pid needed + * @channel: Hardware DMA channel for this pipe + * @split_sc_frame: The low order bits of the frame number the split + * complete should be sent on + */ +struct cvmx_usb_pipe { + struct list_head node; + struct list_head transactions; + u64 interval; + u64 next_tx_frame; + enum cvmx_usb_pipe_flags flags; + enum cvmx_usb_speed device_speed; + enum cvmx_usb_transfer transfer_type; + enum cvmx_usb_direction transfer_dir; + int multi_count; + u16 max_packet; + u8 device_addr; + u8 endpoint_num; + u8 hub_device_addr; + u8 hub_port; + u8 pid_toggle; + u8 channel; + s8 split_sc_frame; +}; + +struct cvmx_usb_tx_fifo { + struct { + int channel; + int size; + u64 address; + } entry[MAX_CHANNELS + 1]; + int head; + int tail; +}; + +/** + * struct octeon_hcd - the state of the USB block + * + * lock: Serialization lock. + * init_flags: Flags passed to initialize. + * index: Which USB block this is for. + * idle_hardware_channels: Bit set for every idle hardware channel. + * usbcx_hprt: Stored port status so we don't need to read a CSR to + * determine splits. + * pipe_for_channel: Map channels to pipes. + * pipe: Storage for pipes. + * indent: Used by debug output to indent functions. + * port_status: Last port status used for change notification. + * idle_pipes: List of open pipes that have no transactions. + * active_pipes: Active pipes indexed by transfer type. + * frame_number: Increments every SOF interrupt for time keeping. + * active_split: Points to the current active split, or NULL. + */ +struct octeon_hcd { + spinlock_t lock; /* serialization lock */ + int init_flags; + int index; + int idle_hardware_channels; + union cvmx_usbcx_hprt usbcx_hprt; + struct cvmx_usb_pipe *pipe_for_channel[MAX_CHANNELS]; + int indent; + struct cvmx_usb_port_status port_status; + struct list_head idle_pipes; + struct list_head active_pipes[4]; + u64 frame_number; + struct cvmx_usb_transaction *active_split; + struct cvmx_usb_tx_fifo periodic; + struct cvmx_usb_tx_fifo nonperiodic; +}; + +/* + * This macro logically sets a single field in a CSR. It does the sequence + * read, modify, and write + */ +#define USB_SET_FIELD32(address, _union, field, value) \ + do { \ + union _union c; \ + \ + c.u32 = cvmx_usb_read_csr32(usb, address); \ + c.s.field = value; \ + cvmx_usb_write_csr32(usb, address, c.u32); \ + } while (0) + +/* Returns the IO address to push/pop stuff data from the FIFOs */ +#define USB_FIFO_ADDRESS(channel, usb_index) \ + (CVMX_USBCX_GOTGCTL(usb_index) + ((channel) + 1) * 0x1000) + +/** + * struct octeon_temp_buffer - a bounce buffer for USB transfers + * @orig_buffer: the original buffer passed by the USB stack + * @data: the newly allocated temporary buffer (excluding meta-data) + * + * Both the DMA engine and FIFO mode will always transfer full 32-bit words. If + * the buffer is too short, we need to allocate a temporary one, and this struct + * represents it. + */ +struct octeon_temp_buffer { + void *orig_buffer; + u8 data[0]; +}; + +static inline struct usb_hcd *octeon_to_hcd(struct octeon_hcd *p) +{ + return container_of((void *)p, struct usb_hcd, hcd_priv); +} + +/** + * octeon_alloc_temp_buffer - allocate a temporary buffer for USB transfer + * (if needed) + * @urb: URB. + * @mem_flags: Memory allocation flags. + * + * This function allocates a temporary bounce buffer whenever it's needed + * due to HW limitations. + */ +static int octeon_alloc_temp_buffer(struct urb *urb, gfp_t mem_flags) +{ + struct octeon_temp_buffer *temp; + + if (urb->num_sgs || urb->sg || + (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) || + !(urb->transfer_buffer_length % sizeof(u32))) + return 0; + + temp = kmalloc(ALIGN(urb->transfer_buffer_length, sizeof(u32)) + + sizeof(*temp), mem_flags); + if (!temp) + return -ENOMEM; + + temp->orig_buffer = urb->transfer_buffer; + if (usb_urb_dir_out(urb)) + memcpy(temp->data, urb->transfer_buffer, + urb->transfer_buffer_length); + urb->transfer_buffer = temp->data; + urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; + + return 0; +} + +/** + * octeon_free_temp_buffer - free a temporary buffer used by USB transfers. + * @urb: URB. + * + * Frees a buffer allocated by octeon_alloc_temp_buffer(). + */ +static void octeon_free_temp_buffer(struct urb *urb) +{ + struct octeon_temp_buffer *temp; + size_t length; + + if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) + return; + + temp = container_of(urb->transfer_buffer, struct octeon_temp_buffer, + data); + if (usb_urb_dir_in(urb)) { + if (usb_pipeisoc(urb->pipe)) + length = urb->transfer_buffer_length; + else + length = urb->actual_length; + + memcpy(temp->orig_buffer, urb->transfer_buffer, length); + } + urb->transfer_buffer = temp->orig_buffer; + urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; + kfree(temp); +} + +/** + * octeon_map_urb_for_dma - Octeon-specific map_urb_for_dma(). + * @hcd: USB HCD structure. + * @urb: URB. + * @mem_flags: Memory allocation flags. + */ +static int octeon_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + int ret; + + ret = octeon_alloc_temp_buffer(urb, mem_flags); + if (ret) + return ret; + + ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + if (ret) + octeon_free_temp_buffer(urb); + + return ret; +} + +/** + * octeon_unmap_urb_for_dma - Octeon-specific unmap_urb_for_dma() + * @hcd: USB HCD structure. + * @urb: URB. + */ +static void octeon_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + usb_hcd_unmap_urb_for_dma(hcd, urb); + octeon_free_temp_buffer(urb); +} + +/** + * Read a USB 32bit CSR. It performs the necessary address swizzle + * for 32bit CSRs and logs the value in a readable format if + * debugging is on. + * + * @usb: USB block this access is for + * @address: 64bit address to read + * + * Returns: Result of the read + */ +static inline u32 cvmx_usb_read_csr32(struct octeon_hcd *usb, u64 address) +{ + return cvmx_read64_uint32(address ^ 4); +} + +/** + * Write a USB 32bit CSR. It performs the necessary address + * swizzle for 32bit CSRs and logs the value in a readable format + * if debugging is on. + * + * @usb: USB block this access is for + * @address: 64bit address to write + * @value: Value to write + */ +static inline void cvmx_usb_write_csr32(struct octeon_hcd *usb, + u64 address, u32 value) +{ + cvmx_write64_uint32(address ^ 4, value); + cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index)); +} + +/** + * Return non zero if this pipe connects to a non HIGH speed + * device through a high speed hub. + * + * @usb: USB block this access is for + * @pipe: Pipe to check + * + * Returns: Non zero if we need to do split transactions + */ +static inline int cvmx_usb_pipe_needs_split(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe) +{ + return pipe->device_speed != CVMX_USB_SPEED_HIGH && + usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH; +} + +/** + * Trivial utility function to return the correct PID for a pipe + * + * @pipe: pipe to check + * + * Returns: PID for pipe + */ +static inline int cvmx_usb_get_data_pid(struct cvmx_usb_pipe *pipe) +{ + if (pipe->pid_toggle) + return 2; /* Data1 */ + return 0; /* Data0 */ +} + +/* Loops through register until txfflsh or rxfflsh become zero.*/ +static int cvmx_wait_tx_rx(struct octeon_hcd *usb, int fflsh_type) +{ + int result; + u64 address = CVMX_USBCX_GRSTCTL(usb->index); + u64 done = cvmx_get_cycle() + 100 * + (u64)octeon_get_clock_rate / 1000000; + union cvmx_usbcx_grstctl c; + + while (1) { + c.u32 = cvmx_usb_read_csr32(usb, address); + if (fflsh_type == 0 && c.s.txfflsh == 0) { + result = 0; + break; + } else if (fflsh_type == 1 && c.s.rxfflsh == 0) { + result = 0; + break; + } else if (cvmx_get_cycle() > done) { + result = -1; + break; + } + + __delay(100); + } + return result; +} + +static void cvmx_fifo_setup(struct octeon_hcd *usb) +{ + union cvmx_usbcx_ghwcfg3 usbcx_ghwcfg3; + union cvmx_usbcx_gnptxfsiz npsiz; + union cvmx_usbcx_hptxfsiz psiz; + + usbcx_ghwcfg3.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_GHWCFG3(usb->index)); + + /* + * Program the USBC_GRXFSIZ register to select the size of the receive + * FIFO (25%). + */ + USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz, + rxfdep, usbcx_ghwcfg3.s.dfifodepth / 4); + + /* + * Program the USBC_GNPTXFSIZ register to select the size and the start + * address of the non-periodic transmit FIFO for nonperiodic + * transactions (50%). + */ + npsiz.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index)); + npsiz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2; + npsiz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4; + cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), npsiz.u32); + + /* + * Program the USBC_HPTXFSIZ register to select the size and start + * address of the periodic transmit FIFO for periodic transactions + * (25%). + */ + psiz.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index)); + psiz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4; + psiz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4; + cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index), psiz.u32); + + /* Flush all FIFOs */ + USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), + cvmx_usbcx_grstctl, txfnum, 0x10); + USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), + cvmx_usbcx_grstctl, txfflsh, 1); + cvmx_wait_tx_rx(usb, 0); + USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index), + cvmx_usbcx_grstctl, rxfflsh, 1); + cvmx_wait_tx_rx(usb, 1); +} + +/** + * Shutdown a USB port after a call to cvmx_usb_initialize(). + * The port should be disabled with all pipes closed when this + * function is called. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * + * Returns: 0 or a negative error code. + */ +static int cvmx_usb_shutdown(struct octeon_hcd *usb) +{ + union cvmx_usbnx_clk_ctl usbn_clk_ctl; + + /* Make sure all pipes are closed */ + if (!list_empty(&usb->idle_pipes) || + !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS]) || + !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT]) || + !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_CONTROL]) || + !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_BULK])) + return -EBUSY; + + /* Disable the clocks and put them in power on reset */ + usbn_clk_ctl.u64 = cvmx_read64_uint64(CVMX_USBNX_CLK_CTL(usb->index)); + usbn_clk_ctl.s.enable = 1; + usbn_clk_ctl.s.por = 1; + usbn_clk_ctl.s.hclk_rst = 1; + usbn_clk_ctl.s.prst = 0; + usbn_clk_ctl.s.hrst = 0; + cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); + return 0; +} + +/** + * Initialize a USB port for use. This must be called before any + * other access to the Octeon USB port is made. The port starts + * off in the disabled state. + * + * @dev: Pointer to struct device for logging purposes. + * @usb: Pointer to struct octeon_hcd. + * + * Returns: 0 or a negative error code. + */ +static int cvmx_usb_initialize(struct device *dev, + struct octeon_hcd *usb) +{ + int channel; + int divisor; + int retries = 0; + union cvmx_usbcx_hcfg usbcx_hcfg; + union cvmx_usbnx_clk_ctl usbn_clk_ctl; + union cvmx_usbcx_gintsts usbc_gintsts; + union cvmx_usbcx_gahbcfg usbcx_gahbcfg; + union cvmx_usbcx_gintmsk usbcx_gintmsk; + union cvmx_usbcx_gusbcfg usbcx_gusbcfg; + union cvmx_usbnx_usbp_ctl_status usbn_usbp_ctl_status; + +retry: + /* + * Power On Reset and PHY Initialization + * + * 1. Wait for DCOK to assert (nothing to do) + * + * 2a. Write USBN0/1_CLK_CTL[POR] = 1 and + * USBN0/1_CLK_CTL[HRST,PRST,HCLK_RST] = 0 + */ + usbn_clk_ctl.u64 = cvmx_read64_uint64(CVMX_USBNX_CLK_CTL(usb->index)); + usbn_clk_ctl.s.por = 1; + usbn_clk_ctl.s.hrst = 0; + usbn_clk_ctl.s.prst = 0; + usbn_clk_ctl.s.hclk_rst = 0; + usbn_clk_ctl.s.enable = 0; + /* + * 2b. Select the USB reference clock/crystal parameters by writing + * appropriate values to USBN0/1_CLK_CTL[P_C_SEL, P_RTYPE, P_COM_ON] + */ + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND) { + /* + * The USB port uses 12/24/48MHz 2.5V board clock + * source at USB_XO. USB_XI should be tied to GND. + * Most Octeon evaluation boards require this setting + */ + if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || + OCTEON_IS_MODEL(OCTEON_CN56XX) || + OCTEON_IS_MODEL(OCTEON_CN50XX)) + /* From CN56XX,CN50XX,CN31XX,CN30XX manuals */ + usbn_clk_ctl.s.p_rtype = 2; /* p_rclk=1 & p_xenbn=0 */ + else + /* From CN52XX manual */ + usbn_clk_ctl.s.p_rtype = 1; + + switch (usb->init_flags & + CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK) { + case CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ: + usbn_clk_ctl.s.p_c_sel = 0; + break; + case CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ: + usbn_clk_ctl.s.p_c_sel = 1; + break; + case CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ: + usbn_clk_ctl.s.p_c_sel = 2; + break; + } + } else { + /* + * The USB port uses a 12MHz crystal as clock source + * at USB_XO and USB_XI + */ + if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) + /* From CN31XX,CN30XX manual */ + usbn_clk_ctl.s.p_rtype = 3; /* p_rclk=1 & p_xenbn=1 */ + else + /* From CN56XX,CN52XX,CN50XX manuals. */ + usbn_clk_ctl.s.p_rtype = 0; + + usbn_clk_ctl.s.p_c_sel = 0; + } + /* + * 2c. Select the HCLK via writing USBN0/1_CLK_CTL[DIVIDE, DIVIDE2] and + * setting USBN0/1_CLK_CTL[ENABLE] = 1. Divide the core clock down + * such that USB is as close as possible to 125Mhz + */ + divisor = DIV_ROUND_UP(octeon_get_clock_rate(), 125000000); + /* Lower than 4 doesn't seem to work properly */ + if (divisor < 4) + divisor = 4; + usbn_clk_ctl.s.divide = divisor; + usbn_clk_ctl.s.divide2 = 0; + cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); + + /* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */ + usbn_clk_ctl.s.hclk_rst = 1; + cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); + /* 2e. Wait 64 core-clock cycles for HCLK to stabilize */ + __delay(64); + /* + * 3. Program the power-on reset field in the USBN clock-control + * register: + * USBN_CLK_CTL[POR] = 0 + */ + usbn_clk_ctl.s.por = 0; + cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); + /* 4. Wait 1 ms for PHY clock to start */ + mdelay(1); + /* + * 5. Program the Reset input from automatic test equipment field in the + * USBP control and status register: + * USBN_USBP_CTL_STATUS[ATE_RESET] = 1 + */ + usbn_usbp_ctl_status.u64 = + cvmx_read64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index)); + usbn_usbp_ctl_status.s.ate_reset = 1; + cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index), + usbn_usbp_ctl_status.u64); + /* 6. Wait 10 cycles */ + __delay(10); + /* + * 7. Clear ATE_RESET field in the USBN clock-control register: + * USBN_USBP_CTL_STATUS[ATE_RESET] = 0 + */ + usbn_usbp_ctl_status.s.ate_reset = 0; + cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index), + usbn_usbp_ctl_status.u64); + /* + * 8. Program the PHY reset field in the USBN clock-control register: + * USBN_CLK_CTL[PRST] = 1 + */ + usbn_clk_ctl.s.prst = 1; + cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); + /* + * 9. Program the USBP control and status register to select host or + * device mode. USBN_USBP_CTL_STATUS[HST_MODE] = 0 for host, = 1 for + * device + */ + usbn_usbp_ctl_status.s.hst_mode = 0; + cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index), + usbn_usbp_ctl_status.u64); + /* 10. Wait 1 us */ + udelay(1); + /* + * 11. Program the hreset_n field in the USBN clock-control register: + * USBN_CLK_CTL[HRST] = 1 + */ + usbn_clk_ctl.s.hrst = 1; + cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); + /* 12. Proceed to USB core initialization */ + usbn_clk_ctl.s.enable = 1; + cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64); + udelay(1); + + /* + * USB Core Initialization + * + * 1. Read USBC_GHWCFG1, USBC_GHWCFG2, USBC_GHWCFG3, USBC_GHWCFG4 to + * determine USB core configuration parameters. + * + * Nothing needed + * + * 2. Program the following fields in the global AHB configuration + * register (USBC_GAHBCFG) + * DMA mode, USBC_GAHBCFG[DMAEn]: 1 = DMA mode, 0 = slave mode + * Burst length, USBC_GAHBCFG[HBSTLEN] = 0 + * Nonperiodic TxFIFO empty level (slave mode only), + * USBC_GAHBCFG[NPTXFEMPLVL] + * Periodic TxFIFO empty level (slave mode only), + * USBC_GAHBCFG[PTXFEMPLVL] + * Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1 + */ + usbcx_gahbcfg.u32 = 0; + usbcx_gahbcfg.s.dmaen = !(usb->init_flags & + CVMX_USB_INITIALIZE_FLAGS_NO_DMA); + usbcx_gahbcfg.s.hbstlen = 0; + usbcx_gahbcfg.s.nptxfemplvl = 1; + usbcx_gahbcfg.s.ptxfemplvl = 1; + usbcx_gahbcfg.s.glblintrmsk = 1; + cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index), + usbcx_gahbcfg.u32); + + /* + * 3. Program the following fields in USBC_GUSBCFG register. + * HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0 + * ULPI DDR select, USBC_GUSBCFG[DDRSEL] = 0 + * USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5 + * PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0 + */ + usbcx_gusbcfg.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_GUSBCFG(usb->index)); + usbcx_gusbcfg.s.toutcal = 0; + usbcx_gusbcfg.s.ddrsel = 0; + usbcx_gusbcfg.s.usbtrdtim = 0x5; + usbcx_gusbcfg.s.phylpwrclksel = 0; + cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index), + usbcx_gusbcfg.u32); + + /* + * 4. The software must unmask the following bits in the USBC_GINTMSK + * register. + * OTG interrupt mask, USBC_GINTMSK[OTGINTMSK] = 1 + * Mode mismatch interrupt mask, USBC_GINTMSK[MODEMISMSK] = 1 + */ + usbcx_gintmsk.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_GINTMSK(usb->index)); + usbcx_gintmsk.s.otgintmsk = 1; + usbcx_gintmsk.s.modemismsk = 1; + usbcx_gintmsk.s.hchintmsk = 1; + usbcx_gintmsk.s.sofmsk = 0; + /* We need RX FIFO interrupts if we don't have DMA */ + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) + usbcx_gintmsk.s.rxflvlmsk = 1; + cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index), + usbcx_gintmsk.u32); + + /* + * Disable all channel interrupts. We'll enable them per channel later. + */ + for (channel = 0; channel < 8; channel++) + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCINTMSKX(channel, usb->index), + 0); + + /* + * Host Port Initialization + * + * 1. Program the host-port interrupt-mask field to unmask, + * USBC_GINTMSK[PRTINT] = 1 + */ + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, prtintmsk, 1); + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, disconnintmsk, 1); + + /* + * 2. Program the USBC_HCFG register to select full-speed host + * or high-speed host. + */ + usbcx_hcfg.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index)); + usbcx_hcfg.s.fslssupp = 0; + usbcx_hcfg.s.fslspclksel = 0; + cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index), usbcx_hcfg.u32); + + cvmx_fifo_setup(usb); + + /* + * If the controller is getting port events right after the reset, it + * means the initialization failed. Try resetting the controller again + * in such case. This is seen to happen after cold boot on DSR-1000N. + */ + usbc_gintsts.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_GINTSTS(usb->index)); + cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), + usbc_gintsts.u32); + dev_dbg(dev, "gintsts after reset: 0x%x\n", (int)usbc_gintsts.u32); + if (!usbc_gintsts.s.disconnint && !usbc_gintsts.s.prtint) + return 0; + if (retries++ >= 5) + return -EAGAIN; + dev_info(dev, "controller reset failed (gintsts=0x%x) - retrying\n", + (int)usbc_gintsts.u32); + msleep(50); + cvmx_usb_shutdown(usb); + msleep(50); + goto retry; +} + +/** + * Reset a USB port. After this call succeeds, the USB port is + * online and servicing requests. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + */ +static void cvmx_usb_reset_port(struct octeon_hcd *usb) +{ + usb->usbcx_hprt.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HPRT(usb->index)); + + /* Program the port reset bit to start the reset process */ + USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt, + prtrst, 1); + + /* + * Wait at least 50ms (high speed), or 10ms (full speed) for the reset + * process to complete. + */ + mdelay(50); + + /* Program the port reset bit to 0, USBC_HPRT[PRTRST] = 0 */ + USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt, + prtrst, 0); + + /* + * Read the port speed field to get the enumerated speed, + * USBC_HPRT[PRTSPD]. + */ + usb->usbcx_hprt.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HPRT(usb->index)); +} + +/** + * Disable a USB port. After this call the USB port will not + * generate data transfers and will not generate events. + * Transactions in process will fail and call their + * associated callbacks. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * + * Returns: 0 or a negative error code. + */ +static int cvmx_usb_disable(struct octeon_hcd *usb) +{ + /* Disable the port */ + USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt, + prtena, 1); + return 0; +} + +/** + * Get the current state of the USB port. Use this call to + * determine if the usb port has anything connected, is enabled, + * or has some sort of error condition. The return value of this + * call has "changed" bits to signal of the value of some fields + * have changed between calls. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * + * Returns: Port status information + */ +static struct cvmx_usb_port_status cvmx_usb_get_status(struct octeon_hcd *usb) +{ + union cvmx_usbcx_hprt usbc_hprt; + struct cvmx_usb_port_status result; + + memset(&result, 0, sizeof(result)); + + usbc_hprt.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index)); + result.port_enabled = usbc_hprt.s.prtena; + result.port_over_current = usbc_hprt.s.prtovrcurract; + result.port_powered = usbc_hprt.s.prtpwr; + result.port_speed = usbc_hprt.s.prtspd; + result.connected = usbc_hprt.s.prtconnsts; + result.connect_change = + result.connected != usb->port_status.connected; + + return result; +} + +/** + * Open a virtual pipe between the host and a USB device. A pipe + * must be opened before data can be transferred between a device + * and Octeon. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @device_addr: + * USB device address to open the pipe to + * (0-127). + * @endpoint_num: + * USB endpoint number to open the pipe to + * (0-15). + * @device_speed: + * The speed of the device the pipe is going + * to. This must match the device's speed, + * which may be different than the port speed. + * @max_packet: The maximum packet length the device can + * transmit/receive (low speed=0-8, full + * speed=0-1023, high speed=0-1024). This value + * comes from the standard endpoint descriptor + * field wMaxPacketSize bits <10:0>. + * @transfer_type: + * The type of transfer this pipe is for. + * @transfer_dir: + * The direction the pipe is in. This is not + * used for control pipes. + * @interval: For ISOCHRONOUS and INTERRUPT transfers, + * this is how often the transfer is scheduled + * for. All other transfers should specify + * zero. The units are in frames (8000/sec at + * high speed, 1000/sec for full speed). + * @multi_count: + * For high speed devices, this is the maximum + * allowed number of packet per microframe. + * Specify zero for non high speed devices. This + * value comes from the standard endpoint descriptor + * field wMaxPacketSize bits <12:11>. + * @hub_device_addr: + * Hub device address this device is connected + * to. Devices connected directly to Octeon + * use zero. This is only used when the device + * is full/low speed behind a high speed hub. + * The address will be of the high speed hub, + * not and full speed hubs after it. + * @hub_port: Which port on the hub the device is + * connected. Use zero for devices connected + * directly to Octeon. Like hub_device_addr, + * this is only used for full/low speed + * devices behind a high speed hub. + * + * Returns: A non-NULL value is a pipe. NULL means an error. + */ +static struct cvmx_usb_pipe *cvmx_usb_open_pipe(struct octeon_hcd *usb, + int device_addr, + int endpoint_num, + enum cvmx_usb_speed + device_speed, + int max_packet, + enum cvmx_usb_transfer + transfer_type, + enum cvmx_usb_direction + transfer_dir, + int interval, int multi_count, + int hub_device_addr, + int hub_port) +{ + struct cvmx_usb_pipe *pipe; + + pipe = kzalloc(sizeof(*pipe), GFP_ATOMIC); + if (!pipe) + return NULL; + if ((device_speed == CVMX_USB_SPEED_HIGH) && + (transfer_dir == CVMX_USB_DIRECTION_OUT) && + (transfer_type == CVMX_USB_TRANSFER_BULK)) + pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING; + pipe->device_addr = device_addr; + pipe->endpoint_num = endpoint_num; + pipe->device_speed = device_speed; + pipe->max_packet = max_packet; + pipe->transfer_type = transfer_type; + pipe->transfer_dir = transfer_dir; + INIT_LIST_HEAD(&pipe->transactions); + + /* + * All pipes use interval to rate limit NAK processing. Force an + * interval if one wasn't supplied + */ + if (!interval) + interval = 1; + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + pipe->interval = interval * 8; + /* Force start splits to be schedule on uFrame 0 */ + pipe->next_tx_frame = ((usb->frame_number + 7) & ~7) + + pipe->interval; + } else { + pipe->interval = interval; + pipe->next_tx_frame = usb->frame_number + pipe->interval; + } + pipe->multi_count = multi_count; + pipe->hub_device_addr = hub_device_addr; + pipe->hub_port = hub_port; + pipe->pid_toggle = 0; + pipe->split_sc_frame = -1; + list_add_tail(&pipe->node, &usb->idle_pipes); + + /* + * We don't need to tell the hardware about this pipe yet since + * it doesn't have any submitted requests + */ + + return pipe; +} + +/** + * Poll the RX FIFOs and remove data as needed. This function is only used + * in non DMA mode. It is very important that this function be called quickly + * enough to prevent FIFO overflow. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + */ +static void cvmx_usb_poll_rx_fifo(struct octeon_hcd *usb) +{ + union cvmx_usbcx_grxstsph rx_status; + int channel; + int bytes; + u64 address; + u32 *ptr; + + rx_status.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_GRXSTSPH(usb->index)); + /* Only read data if IN data is there */ + if (rx_status.s.pktsts != 2) + return; + /* Check if no data is available */ + if (!rx_status.s.bcnt) + return; + + channel = rx_status.s.chnum; + bytes = rx_status.s.bcnt; + if (!bytes) + return; + + /* Get where the DMA engine would have written this data */ + address = cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index) + + channel * 8); + + ptr = cvmx_phys_to_ptr(address); + cvmx_write64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel * 8, + address + bytes); + + /* Loop writing the FIFO data for this packet into memory */ + while (bytes > 0) { + *ptr++ = cvmx_usb_read_csr32(usb, + USB_FIFO_ADDRESS(channel, usb->index)); + bytes -= 4; + } + CVMX_SYNCW; +} + +/** + * Fill the TX hardware fifo with data out of the software + * fifos + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @fifo: Software fifo to use + * @available: Amount of space in the hardware fifo + * + * Returns: Non zero if the hardware fifo was too small and needs + * to be serviced again. + */ +static int cvmx_usb_fill_tx_hw(struct octeon_hcd *usb, + struct cvmx_usb_tx_fifo *fifo, int available) +{ + /* + * We're done either when there isn't anymore space or the software FIFO + * is empty + */ + while (available && (fifo->head != fifo->tail)) { + int i = fifo->tail; + const u32 *ptr = cvmx_phys_to_ptr(fifo->entry[i].address); + u64 csr_address = USB_FIFO_ADDRESS(fifo->entry[i].channel, + usb->index) ^ 4; + int words = available; + + /* Limit the amount of data to what the SW fifo has */ + if (fifo->entry[i].size <= available) { + words = fifo->entry[i].size; + fifo->tail++; + if (fifo->tail > MAX_CHANNELS) + fifo->tail = 0; + } + + /* Update the next locations and counts */ + available -= words; + fifo->entry[i].address += words * 4; + fifo->entry[i].size -= words; + + /* + * Write the HW fifo data. The read every three writes is due + * to an errata on CN3XXX chips + */ + while (words > 3) { + cvmx_write64_uint32(csr_address, *ptr++); + cvmx_write64_uint32(csr_address, *ptr++); + cvmx_write64_uint32(csr_address, *ptr++); + cvmx_read64_uint64( + CVMX_USBNX_DMA0_INB_CHN0(usb->index)); + words -= 3; + } + cvmx_write64_uint32(csr_address, *ptr++); + if (--words) { + cvmx_write64_uint32(csr_address, *ptr++); + if (--words) + cvmx_write64_uint32(csr_address, *ptr++); + } + cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index)); + } + return fifo->head != fifo->tail; +} + +/** + * Check the hardware FIFOs and fill them as needed + * + * @usb: USB device state populated by cvmx_usb_initialize(). + */ +static void cvmx_usb_poll_tx_fifo(struct octeon_hcd *usb) +{ + if (usb->periodic.head != usb->periodic.tail) { + union cvmx_usbcx_hptxsts tx_status; + + tx_status.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HPTXSTS(usb->index)); + if (cvmx_usb_fill_tx_hw(usb, &usb->periodic, + tx_status.s.ptxfspcavail)) + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, ptxfempmsk, 1); + else + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, ptxfempmsk, 0); + } + + if (usb->nonperiodic.head != usb->nonperiodic.tail) { + union cvmx_usbcx_gnptxsts tx_status; + + tx_status.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_GNPTXSTS(usb->index)); + if (cvmx_usb_fill_tx_hw(usb, &usb->nonperiodic, + tx_status.s.nptxfspcavail)) + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, nptxfempmsk, 1); + else + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, nptxfempmsk, 0); + } +} + +/** + * Fill the TX FIFO with an outgoing packet + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @channel: Channel number to get packet from + */ +static void cvmx_usb_fill_tx_fifo(struct octeon_hcd *usb, int channel) +{ + union cvmx_usbcx_hccharx hcchar; + union cvmx_usbcx_hcspltx usbc_hcsplt; + union cvmx_usbcx_hctsizx usbc_hctsiz; + struct cvmx_usb_tx_fifo *fifo; + + /* We only need to fill data on outbound channels */ + hcchar.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCCHARX(channel, usb->index)); + if (hcchar.s.epdir != CVMX_USB_DIRECTION_OUT) + return; + + /* OUT Splits only have data on the start and not the complete */ + usbc_hcsplt.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCSPLTX(channel, usb->index)); + if (usbc_hcsplt.s.spltena && usbc_hcsplt.s.compsplt) + return; + + /* + * Find out how many bytes we need to fill and convert it into 32bit + * words. + */ + usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCTSIZX(channel, usb->index)); + if (!usbc_hctsiz.s.xfersize) + return; + + if ((hcchar.s.eptype == CVMX_USB_TRANSFER_INTERRUPT) || + (hcchar.s.eptype == CVMX_USB_TRANSFER_ISOCHRONOUS)) + fifo = &usb->periodic; + else + fifo = &usb->nonperiodic; + + fifo->entry[fifo->head].channel = channel; + fifo->entry[fifo->head].address = + cvmx_read64_uint64(CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + + channel * 8); + fifo->entry[fifo->head].size = (usbc_hctsiz.s.xfersize + 3) >> 2; + fifo->head++; + if (fifo->head > MAX_CHANNELS) + fifo->head = 0; + + cvmx_usb_poll_tx_fifo(usb); +} + +/** + * Perform channel specific setup for Control transactions. All + * the generic stuff will already have been done in cvmx_usb_start_channel(). + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @channel: Channel to setup + * @pipe: Pipe for control transaction + */ +static void cvmx_usb_start_channel_control(struct octeon_hcd *usb, + int channel, + struct cvmx_usb_pipe *pipe) +{ + struct usb_hcd *hcd = octeon_to_hcd(usb); + struct device *dev = hcd->self.controller; + struct cvmx_usb_transaction *transaction = + list_first_entry(&pipe->transactions, typeof(*transaction), + node); + struct usb_ctrlrequest *header = + cvmx_phys_to_ptr(transaction->control_header); + int bytes_to_transfer = transaction->buffer_length - + transaction->actual_bytes; + int packets_to_transfer; + union cvmx_usbcx_hctsizx usbc_hctsiz; + + usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCTSIZX(channel, usb->index)); + + switch (transaction->stage) { + case CVMX_USB_STAGE_NON_CONTROL: + case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE: + dev_err(dev, "%s: ERROR - Non control stage\n", __func__); + break; + case CVMX_USB_STAGE_SETUP: + usbc_hctsiz.s.pid = 3; /* Setup */ + bytes_to_transfer = sizeof(*header); + /* All Control operations start with a setup going OUT */ + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + cvmx_usbcx_hccharx, epdir, + CVMX_USB_DIRECTION_OUT); + /* + * Setup send the control header instead of the buffer data. The + * buffer data will be used in the next stage + */ + cvmx_write64_uint64(CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) + + channel * 8, + transaction->control_header); + break; + case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE: + usbc_hctsiz.s.pid = 3; /* Setup */ + bytes_to_transfer = 0; + /* All Control operations start with a setup going OUT */ + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + cvmx_usbcx_hccharx, epdir, + CVMX_USB_DIRECTION_OUT); + + USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), + cvmx_usbcx_hcspltx, compsplt, 1); + break; + case CVMX_USB_STAGE_DATA: + usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + if (header->bRequestType & USB_DIR_IN) + bytes_to_transfer = 0; + else if (bytes_to_transfer > pipe->max_packet) + bytes_to_transfer = pipe->max_packet; + } + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + cvmx_usbcx_hccharx, epdir, + ((header->bRequestType & USB_DIR_IN) ? + CVMX_USB_DIRECTION_IN : + CVMX_USB_DIRECTION_OUT)); + break; + case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE: + usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); + if (!(header->bRequestType & USB_DIR_IN)) + bytes_to_transfer = 0; + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + cvmx_usbcx_hccharx, epdir, + ((header->bRequestType & USB_DIR_IN) ? + CVMX_USB_DIRECTION_IN : + CVMX_USB_DIRECTION_OUT)); + USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), + cvmx_usbcx_hcspltx, compsplt, 1); + break; + case CVMX_USB_STAGE_STATUS: + usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); + bytes_to_transfer = 0; + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + cvmx_usbcx_hccharx, epdir, + ((header->bRequestType & USB_DIR_IN) ? + CVMX_USB_DIRECTION_OUT : + CVMX_USB_DIRECTION_IN)); + break; + case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE: + usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); + bytes_to_transfer = 0; + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + cvmx_usbcx_hccharx, epdir, + ((header->bRequestType & USB_DIR_IN) ? + CVMX_USB_DIRECTION_OUT : + CVMX_USB_DIRECTION_IN)); + USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index), + cvmx_usbcx_hcspltx, compsplt, 1); + break; + } + + /* + * Make sure the transfer never exceeds the byte limit of the hardware. + * Further bytes will be sent as continued transactions + */ + if (bytes_to_transfer > MAX_TRANSFER_BYTES) { + /* Round MAX_TRANSFER_BYTES to a multiple of out packet size */ + bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet; + bytes_to_transfer *= pipe->max_packet; + } + + /* + * Calculate the number of packets to transfer. If the length is zero + * we still need to transfer one packet + */ + packets_to_transfer = DIV_ROUND_UP(bytes_to_transfer, + pipe->max_packet); + if (packets_to_transfer == 0) { + packets_to_transfer = 1; + } else if ((packets_to_transfer > 1) && + (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)) { + /* + * Limit to one packet when not using DMA. Channels must be + * restarted between every packet for IN transactions, so there + * is no reason to do multiple packets in a row + */ + packets_to_transfer = 1; + bytes_to_transfer = packets_to_transfer * pipe->max_packet; + } else if (packets_to_transfer > MAX_TRANSFER_PACKETS) { + /* + * Limit the number of packet and data transferred to what the + * hardware can handle + */ + packets_to_transfer = MAX_TRANSFER_PACKETS; + bytes_to_transfer = packets_to_transfer * pipe->max_packet; + } + + usbc_hctsiz.s.xfersize = bytes_to_transfer; + usbc_hctsiz.s.pktcnt = packets_to_transfer; + + cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index), + usbc_hctsiz.u32); +} + +/** + * Start a channel to perform the pipe's head transaction + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @channel: Channel to setup + * @pipe: Pipe to start + */ +static void cvmx_usb_start_channel(struct octeon_hcd *usb, int channel, + struct cvmx_usb_pipe *pipe) +{ + struct cvmx_usb_transaction *transaction = + list_first_entry(&pipe->transactions, typeof(*transaction), + node); + + /* Make sure all writes to the DMA region get flushed */ + CVMX_SYNCW; + + /* Attach the channel to the pipe */ + usb->pipe_for_channel[channel] = pipe; + pipe->channel = channel; + pipe->flags |= CVMX_USB_PIPE_FLAGS_SCHEDULED; + + /* Mark this channel as in use */ + usb->idle_hardware_channels &= ~(1 << channel); + + /* Enable the channel interrupt bits */ + { + union cvmx_usbcx_hcintx usbc_hcint; + union cvmx_usbcx_hcintmskx usbc_hcintmsk; + union cvmx_usbcx_haintmsk usbc_haintmsk; + + /* Clear all channel status bits */ + usbc_hcint.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCINTX(channel, usb->index)); + + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCINTX(channel, usb->index), + usbc_hcint.u32); + + usbc_hcintmsk.u32 = 0; + usbc_hcintmsk.s.chhltdmsk = 1; + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) { + /* + * Channels need these extra interrupts when we aren't + * in DMA mode. + */ + usbc_hcintmsk.s.datatglerrmsk = 1; + usbc_hcintmsk.s.frmovrunmsk = 1; + usbc_hcintmsk.s.bblerrmsk = 1; + usbc_hcintmsk.s.xacterrmsk = 1; + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + /* + * Splits don't generate xfercompl, so we need + * ACK and NYET. + */ + usbc_hcintmsk.s.nyetmsk = 1; + usbc_hcintmsk.s.ackmsk = 1; + } + usbc_hcintmsk.s.nakmsk = 1; + usbc_hcintmsk.s.stallmsk = 1; + usbc_hcintmsk.s.xfercomplmsk = 1; + } + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCINTMSKX(channel, usb->index), + usbc_hcintmsk.u32); + + /* Enable the channel interrupt to propagate */ + usbc_haintmsk.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HAINTMSK(usb->index)); + usbc_haintmsk.s.haintmsk |= 1 << channel; + cvmx_usb_write_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index), + usbc_haintmsk.u32); + } + + /* Setup the location the DMA engine uses. */ + { + u64 reg; + u64 dma_address = transaction->buffer + + transaction->actual_bytes; + + if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS) + dma_address = transaction->buffer + + transaction->iso_packets[0].offset + + transaction->actual_bytes; + + if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) + reg = CVMX_USBNX_DMA0_OUTB_CHN0(usb->index); + else + reg = CVMX_USBNX_DMA0_INB_CHN0(usb->index); + cvmx_write64_uint64(reg + channel * 8, dma_address); + } + + /* Setup both the size of the transfer and the SPLIT characteristics */ + { + union cvmx_usbcx_hcspltx usbc_hcsplt = {.u32 = 0}; + union cvmx_usbcx_hctsizx usbc_hctsiz = {.u32 = 0}; + int packets_to_transfer; + int bytes_to_transfer = transaction->buffer_length - + transaction->actual_bytes; + + /* + * ISOCHRONOUS transactions store each individual transfer size + * in the packet structure, not the global buffer_length + */ + if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS) + bytes_to_transfer = + transaction->iso_packets[0].length - + transaction->actual_bytes; + + /* + * We need to do split transactions when we are talking to non + * high speed devices that are behind a high speed hub + */ + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + /* + * On the start split phase (stage is even) record the + * frame number we will need to send the split complete. + * We only store the lower two bits since the time ahead + * can only be two frames + */ + if ((transaction->stage & 1) == 0) { + if (transaction->type == CVMX_USB_TRANSFER_BULK) + pipe->split_sc_frame = + (usb->frame_number + 1) & 0x7f; + else + pipe->split_sc_frame = + (usb->frame_number + 2) & 0x7f; + } else { + pipe->split_sc_frame = -1; + } + + usbc_hcsplt.s.spltena = 1; + usbc_hcsplt.s.hubaddr = pipe->hub_device_addr; + usbc_hcsplt.s.prtaddr = pipe->hub_port; + usbc_hcsplt.s.compsplt = (transaction->stage == + CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE); + + /* + * SPLIT transactions can only ever transmit one data + * packet so limit the transfer size to the max packet + * size + */ + if (bytes_to_transfer > pipe->max_packet) + bytes_to_transfer = pipe->max_packet; + + /* + * ISOCHRONOUS OUT splits are unique in that they limit + * data transfers to 188 byte chunks representing the + * begin/middle/end of the data or all + */ + if (!usbc_hcsplt.s.compsplt && + (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) && + (pipe->transfer_type == + CVMX_USB_TRANSFER_ISOCHRONOUS)) { + /* + * Clear the split complete frame number as + * there isn't going to be a split complete + */ + pipe->split_sc_frame = -1; + /* + * See if we've started this transfer and sent + * data + */ + if (transaction->actual_bytes == 0) { + /* + * Nothing sent yet, this is either a + * begin or the entire payload + */ + if (bytes_to_transfer <= 188) + /* Entire payload in one go */ + usbc_hcsplt.s.xactpos = 3; + else + /* First part of payload */ + usbc_hcsplt.s.xactpos = 2; + } else { + /* + * Continuing the previous data, we must + * either be in the middle or at the end + */ + if (bytes_to_transfer <= 188) + /* End of payload */ + usbc_hcsplt.s.xactpos = 1; + else + /* Middle of payload */ + usbc_hcsplt.s.xactpos = 0; + } + /* + * Again, the transfer size is limited to 188 + * bytes + */ + if (bytes_to_transfer > 188) + bytes_to_transfer = 188; + } + } + + /* + * Make sure the transfer never exceeds the byte limit of the + * hardware. Further bytes will be sent as continued + * transactions + */ + if (bytes_to_transfer > MAX_TRANSFER_BYTES) { + /* + * Round MAX_TRANSFER_BYTES to a multiple of out packet + * size + */ + bytes_to_transfer = MAX_TRANSFER_BYTES / + pipe->max_packet; + bytes_to_transfer *= pipe->max_packet; + } + + /* + * Calculate the number of packets to transfer. If the length is + * zero we still need to transfer one packet + */ + packets_to_transfer = + DIV_ROUND_UP(bytes_to_transfer, pipe->max_packet); + if (packets_to_transfer == 0) { + packets_to_transfer = 1; + } else if ((packets_to_transfer > 1) && + (usb->init_flags & + CVMX_USB_INITIALIZE_FLAGS_NO_DMA)) { + /* + * Limit to one packet when not using DMA. Channels must + * be restarted between every packet for IN + * transactions, so there is no reason to do multiple + * packets in a row + */ + packets_to_transfer = 1; + bytes_to_transfer = packets_to_transfer * + pipe->max_packet; + } else if (packets_to_transfer > MAX_TRANSFER_PACKETS) { + /* + * Limit the number of packet and data transferred to + * what the hardware can handle + */ + packets_to_transfer = MAX_TRANSFER_PACKETS; + bytes_to_transfer = packets_to_transfer * + pipe->max_packet; + } + + usbc_hctsiz.s.xfersize = bytes_to_transfer; + usbc_hctsiz.s.pktcnt = packets_to_transfer; + + /* Update the DATA0/DATA1 toggle */ + usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe); + /* + * High speed pipes may need a hardware ping before they start + */ + if (pipe->flags & CVMX_USB_PIPE_FLAGS_NEED_PING) + usbc_hctsiz.s.dopng = 1; + + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCSPLTX(channel, usb->index), + usbc_hcsplt.u32); + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCTSIZX(channel, usb->index), + usbc_hctsiz.u32); + } + + /* Setup the Host Channel Characteristics Register */ + { + union cvmx_usbcx_hccharx usbc_hcchar = {.u32 = 0}; + + /* + * Set the startframe odd/even properly. This is only used for + * periodic + */ + usbc_hcchar.s.oddfrm = usb->frame_number & 1; + + /* + * Set the number of back to back packets allowed by this + * endpoint. Split transactions interpret "ec" as the number of + * immediate retries of failure. These retries happen too + * quickly, so we disable these entirely for splits + */ + if (cvmx_usb_pipe_needs_split(usb, pipe)) + usbc_hcchar.s.ec = 1; + else if (pipe->multi_count < 1) + usbc_hcchar.s.ec = 1; + else if (pipe->multi_count > 3) + usbc_hcchar.s.ec = 3; + else + usbc_hcchar.s.ec = pipe->multi_count; + + /* Set the rest of the endpoint specific settings */ + usbc_hcchar.s.devaddr = pipe->device_addr; + usbc_hcchar.s.eptype = transaction->type; + usbc_hcchar.s.lspddev = + (pipe->device_speed == CVMX_USB_SPEED_LOW); + usbc_hcchar.s.epdir = pipe->transfer_dir; + usbc_hcchar.s.epnum = pipe->endpoint_num; + usbc_hcchar.s.mps = pipe->max_packet; + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCCHARX(channel, usb->index), + usbc_hcchar.u32); + } + + /* Do transaction type specific fixups as needed */ + switch (transaction->type) { + case CVMX_USB_TRANSFER_CONTROL: + cvmx_usb_start_channel_control(usb, channel, pipe); + break; + case CVMX_USB_TRANSFER_BULK: + case CVMX_USB_TRANSFER_INTERRUPT: + break; + case CVMX_USB_TRANSFER_ISOCHRONOUS: + if (!cvmx_usb_pipe_needs_split(usb, pipe)) { + /* + * ISO transactions require different PIDs depending on + * direction and how many packets are needed + */ + if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) { + if (pipe->multi_count < 2) /* Need DATA0 */ + USB_SET_FIELD32( + CVMX_USBCX_HCTSIZX(channel, + usb->index), + cvmx_usbcx_hctsizx, pid, 0); + else /* Need MDATA */ + USB_SET_FIELD32( + CVMX_USBCX_HCTSIZX(channel, + usb->index), + cvmx_usbcx_hctsizx, pid, 3); + } + } + break; + } + { + union cvmx_usbcx_hctsizx usbc_hctsiz = { .u32 = + cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCTSIZX(channel, + usb->index)) + }; + transaction->xfersize = usbc_hctsiz.s.xfersize; + transaction->pktcnt = usbc_hctsiz.s.pktcnt; + } + /* Remember when we start a split transaction */ + if (cvmx_usb_pipe_needs_split(usb, pipe)) + usb->active_split = transaction; + USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index), + cvmx_usbcx_hccharx, chena, 1); + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) + cvmx_usb_fill_tx_fifo(usb, channel); +} + +/** + * Find a pipe that is ready to be scheduled to hardware. + * @usb: USB device state populated by cvmx_usb_initialize(). + * @xfer_type: Transfer type + * + * Returns: Pipe or NULL if none are ready + */ +static struct cvmx_usb_pipe *cvmx_usb_find_ready_pipe(struct octeon_hcd *usb, + enum cvmx_usb_transfer xfer_type) +{ + struct list_head *list = usb->active_pipes + xfer_type; + u64 current_frame = usb->frame_number; + struct cvmx_usb_pipe *pipe; + + list_for_each_entry(pipe, list, node) { + struct cvmx_usb_transaction *t = + list_first_entry(&pipe->transactions, typeof(*t), + node); + if (!(pipe->flags & CVMX_USB_PIPE_FLAGS_SCHEDULED) && t && + (pipe->next_tx_frame <= current_frame) && + ((pipe->split_sc_frame == -1) || + ((((int)current_frame - pipe->split_sc_frame) & 0x7f) < + 0x40)) && + (!usb->active_split || (usb->active_split == t))) { + prefetch(t); + return pipe; + } + } + return NULL; +} + +static struct cvmx_usb_pipe *cvmx_usb_next_pipe(struct octeon_hcd *usb, + int is_sof) +{ + struct cvmx_usb_pipe *pipe; + + /* Find a pipe needing service. */ + if (is_sof) { + /* + * Only process periodic pipes on SOF interrupts. This way we + * are sure that the periodic data is sent in the beginning of + * the frame. + */ + pipe = cvmx_usb_find_ready_pipe(usb, + CVMX_USB_TRANSFER_ISOCHRONOUS); + if (pipe) + return pipe; + pipe = cvmx_usb_find_ready_pipe(usb, + CVMX_USB_TRANSFER_INTERRUPT); + if (pipe) + return pipe; + } + pipe = cvmx_usb_find_ready_pipe(usb, CVMX_USB_TRANSFER_CONTROL); + if (pipe) + return pipe; + return cvmx_usb_find_ready_pipe(usb, CVMX_USB_TRANSFER_BULK); +} + +/** + * Called whenever a pipe might need to be scheduled to the + * hardware. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @is_sof: True if this schedule was called on a SOF interrupt. + */ +static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof) +{ + int channel; + struct cvmx_usb_pipe *pipe; + int need_sof; + enum cvmx_usb_transfer ttype; + + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) { + /* + * Without DMA we need to be careful to not schedule something + * at the end of a frame and cause an overrun. + */ + union cvmx_usbcx_hfnum hfnum = { + .u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HFNUM(usb->index)) + }; + + union cvmx_usbcx_hfir hfir = { + .u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HFIR(usb->index)) + }; + + if (hfnum.s.frrem < hfir.s.frint / 4) + goto done; + } + + while (usb->idle_hardware_channels) { + /* Find an idle channel */ + channel = __fls(usb->idle_hardware_channels); + if (unlikely(channel > 7)) + break; + + pipe = cvmx_usb_next_pipe(usb, is_sof); + if (!pipe) + break; + + cvmx_usb_start_channel(usb, channel, pipe); + } + +done: + /* + * Only enable SOF interrupts when we have transactions pending in the + * future that might need to be scheduled + */ + need_sof = 0; + for (ttype = CVMX_USB_TRANSFER_CONTROL; + ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) { + list_for_each_entry(pipe, &usb->active_pipes[ttype], node) { + if (pipe->next_tx_frame > usb->frame_number) { + need_sof = 1; + break; + } + } + } + USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index), + cvmx_usbcx_gintmsk, sofmsk, need_sof); +} + +static void octeon_usb_urb_complete_callback(struct octeon_hcd *usb, + enum cvmx_usb_status status, + struct cvmx_usb_pipe *pipe, + struct cvmx_usb_transaction + *transaction, + int bytes_transferred, + struct urb *urb) +{ + struct usb_hcd *hcd = octeon_to_hcd(usb); + struct device *dev = hcd->self.controller; + + if (likely(status == CVMX_USB_STATUS_OK)) + urb->actual_length = bytes_transferred; + else + urb->actual_length = 0; + + urb->hcpriv = NULL; + + /* For Isochronous transactions we need to update the URB packet status + * list from data in our private copy + */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + int i; + /* + * The pointer to the private list is stored in the setup_packet + * field. + */ + struct cvmx_usb_iso_packet *iso_packet = + (struct cvmx_usb_iso_packet *)urb->setup_packet; + /* Recalculate the transfer size by adding up each packet */ + urb->actual_length = 0; + for (i = 0; i < urb->number_of_packets; i++) { + if (iso_packet[i].status == CVMX_USB_STATUS_OK) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = + iso_packet[i].length; + urb->actual_length += + urb->iso_frame_desc[i].actual_length; + } else { + dev_dbg(dev, "ISOCHRONOUS packet=%d of %d status=%d pipe=%p transaction=%p size=%d\n", + i, urb->number_of_packets, + iso_packet[i].status, pipe, + transaction, iso_packet[i].length); + urb->iso_frame_desc[i].status = -EREMOTEIO; + } + } + /* Free the private list now that we don't need it anymore */ + kfree(iso_packet); + urb->setup_packet = NULL; + } + + switch (status) { + case CVMX_USB_STATUS_OK: + urb->status = 0; + break; + case CVMX_USB_STATUS_CANCEL: + if (urb->status == 0) + urb->status = -ENOENT; + break; + case CVMX_USB_STATUS_STALL: + dev_dbg(dev, "status=stall pipe=%p transaction=%p size=%d\n", + pipe, transaction, bytes_transferred); + urb->status = -EPIPE; + break; + case CVMX_USB_STATUS_BABBLEERR: + dev_dbg(dev, "status=babble pipe=%p transaction=%p size=%d\n", + pipe, transaction, bytes_transferred); + urb->status = -EPIPE; + break; + case CVMX_USB_STATUS_SHORT: + dev_dbg(dev, "status=short pipe=%p transaction=%p size=%d\n", + pipe, transaction, bytes_transferred); + urb->status = -EREMOTEIO; + break; + case CVMX_USB_STATUS_ERROR: + case CVMX_USB_STATUS_XACTERR: + case CVMX_USB_STATUS_DATATGLERR: + case CVMX_USB_STATUS_FRAMEERR: + dev_dbg(dev, "status=%d pipe=%p transaction=%p size=%d\n", + status, pipe, transaction, bytes_transferred); + urb->status = -EPROTO; + break; + } + usb_hcd_unlink_urb_from_ep(octeon_to_hcd(usb), urb); + spin_unlock(&usb->lock); + usb_hcd_giveback_urb(octeon_to_hcd(usb), urb, urb->status); + spin_lock(&usb->lock); +} + +/** + * Signal the completion of a transaction and free it. The + * transaction will be removed from the pipe transaction list. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @pipe: Pipe the transaction is on + * @transaction: + * Transaction that completed + * @complete_code: + * Completion code + */ +static void cvmx_usb_complete(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct cvmx_usb_transaction *transaction, + enum cvmx_usb_status complete_code) +{ + /* If this was a split then clear our split in progress marker */ + if (usb->active_split == transaction) + usb->active_split = NULL; + + /* + * Isochronous transactions need extra processing as they might not be + * done after a single data transfer + */ + if (unlikely(transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)) { + /* Update the number of bytes transferred in this ISO packet */ + transaction->iso_packets[0].length = transaction->actual_bytes; + transaction->iso_packets[0].status = complete_code; + + /* + * If there are more ISOs pending and we succeeded, schedule the + * next one + */ + if ((transaction->iso_number_packets > 1) && + (complete_code == CVMX_USB_STATUS_OK)) { + /* No bytes transferred for this packet as of yet */ + transaction->actual_bytes = 0; + /* One less ISO waiting to transfer */ + transaction->iso_number_packets--; + /* Increment to the next location in our packet array */ + transaction->iso_packets++; + transaction->stage = CVMX_USB_STAGE_NON_CONTROL; + return; + } + } + + /* Remove the transaction from the pipe list */ + list_del(&transaction->node); + if (list_empty(&pipe->transactions)) + list_move_tail(&pipe->node, &usb->idle_pipes); + octeon_usb_urb_complete_callback(usb, complete_code, pipe, + transaction, + transaction->actual_bytes, + transaction->urb); + kfree(transaction); +} + +/** + * Submit a usb transaction to a pipe. Called for all types + * of transactions. + * + * @usb: + * @pipe: Which pipe to submit to. + * @type: Transaction type + * @buffer: User buffer for the transaction + * @buffer_length: + * User buffer's length in bytes + * @control_header: + * For control transactions, the 8 byte standard header + * @iso_start_frame: + * For ISO transactions, the start frame + * @iso_number_packets: + * For ISO, the number of packet in the transaction. + * @iso_packets: + * A description of each ISO packet + * @urb: URB for the callback + * + * Returns: Transaction or NULL on failure. + */ +static struct cvmx_usb_transaction *cvmx_usb_submit_transaction( + struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + enum cvmx_usb_transfer type, + u64 buffer, + int buffer_length, + u64 control_header, + int iso_start_frame, + int iso_number_packets, + struct cvmx_usb_iso_packet *iso_packets, + struct urb *urb) +{ + struct cvmx_usb_transaction *transaction; + + if (unlikely(pipe->transfer_type != type)) + return NULL; + + transaction = kzalloc(sizeof(*transaction), GFP_ATOMIC); + if (unlikely(!transaction)) + return NULL; + + transaction->type = type; + transaction->buffer = buffer; + transaction->buffer_length = buffer_length; + transaction->control_header = control_header; + /* FIXME: This is not used, implement it. */ + transaction->iso_start_frame = iso_start_frame; + transaction->iso_number_packets = iso_number_packets; + transaction->iso_packets = iso_packets; + transaction->urb = urb; + if (transaction->type == CVMX_USB_TRANSFER_CONTROL) + transaction->stage = CVMX_USB_STAGE_SETUP; + else + transaction->stage = CVMX_USB_STAGE_NON_CONTROL; + + if (!list_empty(&pipe->transactions)) { + list_add_tail(&transaction->node, &pipe->transactions); + } else { + list_add_tail(&transaction->node, &pipe->transactions); + list_move_tail(&pipe->node, + &usb->active_pipes[pipe->transfer_type]); + + /* + * We may need to schedule the pipe if this was the head of the + * pipe. + */ + cvmx_usb_schedule(usb, 0); + } + + return transaction; +} + +/** + * Call to submit a USB Bulk transfer to a pipe. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @pipe: Handle to the pipe for the transfer. + * @urb: URB. + * + * Returns: A submitted transaction or NULL on failure. + */ +static struct cvmx_usb_transaction *cvmx_usb_submit_bulk( + struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct urb *urb) +{ + return cvmx_usb_submit_transaction(usb, pipe, CVMX_USB_TRANSFER_BULK, + urb->transfer_dma, + urb->transfer_buffer_length, + 0, /* control_header */ + 0, /* iso_start_frame */ + 0, /* iso_number_packets */ + NULL, /* iso_packets */ + urb); +} + +/** + * Call to submit a USB Interrupt transfer to a pipe. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @pipe: Handle to the pipe for the transfer. + * @urb: URB returned when the callback is called. + * + * Returns: A submitted transaction or NULL on failure. + */ +static struct cvmx_usb_transaction *cvmx_usb_submit_interrupt( + struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct urb *urb) +{ + return cvmx_usb_submit_transaction(usb, pipe, + CVMX_USB_TRANSFER_INTERRUPT, + urb->transfer_dma, + urb->transfer_buffer_length, + 0, /* control_header */ + 0, /* iso_start_frame */ + 0, /* iso_number_packets */ + NULL, /* iso_packets */ + urb); +} + +/** + * Call to submit a USB Control transfer to a pipe. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @pipe: Handle to the pipe for the transfer. + * @urb: URB. + * + * Returns: A submitted transaction or NULL on failure. + */ +static struct cvmx_usb_transaction *cvmx_usb_submit_control( + struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct urb *urb) +{ + int buffer_length = urb->transfer_buffer_length; + u64 control_header = urb->setup_dma; + struct usb_ctrlrequest *header = cvmx_phys_to_ptr(control_header); + + if ((header->bRequestType & USB_DIR_IN) == 0) + buffer_length = le16_to_cpu(header->wLength); + + return cvmx_usb_submit_transaction(usb, pipe, + CVMX_USB_TRANSFER_CONTROL, + urb->transfer_dma, buffer_length, + control_header, + 0, /* iso_start_frame */ + 0, /* iso_number_packets */ + NULL, /* iso_packets */ + urb); +} + +/** + * Call to submit a USB Isochronous transfer to a pipe. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @pipe: Handle to the pipe for the transfer. + * @urb: URB returned when the callback is called. + * + * Returns: A submitted transaction or NULL on failure. + */ +static struct cvmx_usb_transaction *cvmx_usb_submit_isochronous( + struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct urb *urb) +{ + struct cvmx_usb_iso_packet *packets; + + packets = (struct cvmx_usb_iso_packet *)urb->setup_packet; + return cvmx_usb_submit_transaction(usb, pipe, + CVMX_USB_TRANSFER_ISOCHRONOUS, + urb->transfer_dma, + urb->transfer_buffer_length, + 0, /* control_header */ + urb->start_frame, + urb->number_of_packets, + packets, urb); +} + +/** + * Cancel one outstanding request in a pipe. Canceling a request + * can fail if the transaction has already completed before cancel + * is called. Even after a successful cancel call, it may take + * a frame or two for the cvmx_usb_poll() function to call the + * associated callback. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @pipe: Pipe to cancel requests in. + * @transaction: Transaction to cancel, returned by the submit function. + * + * Returns: 0 or a negative error code. + */ +static int cvmx_usb_cancel(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct cvmx_usb_transaction *transaction) +{ + /* + * If the transaction is the HEAD of the queue and scheduled. We need to + * treat it special + */ + if (list_first_entry(&pipe->transactions, typeof(*transaction), node) == + transaction && (pipe->flags & CVMX_USB_PIPE_FLAGS_SCHEDULED)) { + union cvmx_usbcx_hccharx usbc_hcchar; + + usb->pipe_for_channel[pipe->channel] = NULL; + pipe->flags &= ~CVMX_USB_PIPE_FLAGS_SCHEDULED; + + CVMX_SYNCW; + + usbc_hcchar.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCCHARX(pipe->channel, usb->index)); + /* + * If the channel isn't enabled then the transaction already + * completed. + */ + if (usbc_hcchar.s.chena) { + usbc_hcchar.s.chdis = 1; + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCCHARX(pipe->channel, + usb->index), + usbc_hcchar.u32); + } + } + cvmx_usb_complete(usb, pipe, transaction, CVMX_USB_STATUS_CANCEL); + return 0; +} + +/** + * Cancel all outstanding requests in a pipe. Logically all this + * does is call cvmx_usb_cancel() in a loop. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @pipe: Pipe to cancel requests in. + * + * Returns: 0 or a negative error code. + */ +static int cvmx_usb_cancel_all(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe) +{ + struct cvmx_usb_transaction *transaction, *next; + + /* Simply loop through and attempt to cancel each transaction */ + list_for_each_entry_safe(transaction, next, &pipe->transactions, node) { + int result = cvmx_usb_cancel(usb, pipe, transaction); + + if (unlikely(result != 0)) + return result; + } + return 0; +} + +/** + * Close a pipe created with cvmx_usb_open_pipe(). + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * @pipe: Pipe to close. + * + * Returns: 0 or a negative error code. EBUSY is returned if the pipe has + * outstanding transfers. + */ +static int cvmx_usb_close_pipe(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe) +{ + /* Fail if the pipe has pending transactions */ + if (!list_empty(&pipe->transactions)) + return -EBUSY; + + list_del(&pipe->node); + kfree(pipe); + + return 0; +} + +/** + * Get the current USB protocol level frame number. The frame + * number is always in the range of 0-0x7ff. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * + * Returns: USB frame number + */ +static int cvmx_usb_get_frame_number(struct octeon_hcd *usb) +{ + union cvmx_usbcx_hfnum usbc_hfnum; + + usbc_hfnum.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index)); + + return usbc_hfnum.s.frnum; +} + +static void cvmx_usb_transfer_control(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct cvmx_usb_transaction *transaction, + union cvmx_usbcx_hccharx usbc_hcchar, + int buffer_space_left, + int bytes_in_last_packet) +{ + switch (transaction->stage) { + case CVMX_USB_STAGE_NON_CONTROL: + case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE: + /* This should be impossible */ + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_ERROR); + break; + case CVMX_USB_STAGE_SETUP: + pipe->pid_toggle = 1; + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + transaction->stage = + CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE; + } else { + struct usb_ctrlrequest *header = + cvmx_phys_to_ptr(transaction->control_header); + if (header->wLength) + transaction->stage = CVMX_USB_STAGE_DATA; + else + transaction->stage = CVMX_USB_STAGE_STATUS; + } + break; + case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE: + { + struct usb_ctrlrequest *header = + cvmx_phys_to_ptr(transaction->control_header); + if (header->wLength) + transaction->stage = CVMX_USB_STAGE_DATA; + else + transaction->stage = CVMX_USB_STAGE_STATUS; + } + break; + case CVMX_USB_STAGE_DATA: + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + transaction->stage = CVMX_USB_STAGE_DATA_SPLIT_COMPLETE; + /* + * For setup OUT data that are splits, + * the hardware doesn't appear to count + * transferred data. Here we manually + * update the data transferred + */ + if (!usbc_hcchar.s.epdir) { + if (buffer_space_left < pipe->max_packet) + transaction->actual_bytes += + buffer_space_left; + else + transaction->actual_bytes += + pipe->max_packet; + } + } else if ((buffer_space_left == 0) || + (bytes_in_last_packet < pipe->max_packet)) { + pipe->pid_toggle = 1; + transaction->stage = CVMX_USB_STAGE_STATUS; + } + break; + case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE: + if ((buffer_space_left == 0) || + (bytes_in_last_packet < pipe->max_packet)) { + pipe->pid_toggle = 1; + transaction->stage = CVMX_USB_STAGE_STATUS; + } else { + transaction->stage = CVMX_USB_STAGE_DATA; + } + break; + case CVMX_USB_STAGE_STATUS: + if (cvmx_usb_pipe_needs_split(usb, pipe)) + transaction->stage = + CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE; + else + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_OK); + break; + case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE: + cvmx_usb_complete(usb, pipe, transaction, CVMX_USB_STATUS_OK); + break; + } +} + +static void cvmx_usb_transfer_bulk(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct cvmx_usb_transaction *transaction, + union cvmx_usbcx_hcintx usbc_hcint, + int buffer_space_left, + int bytes_in_last_packet) +{ + /* + * The only time a bulk transfer isn't complete when it finishes with + * an ACK is during a split transaction. For splits we need to continue + * the transfer if more data is needed. + */ + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL) + transaction->stage = + CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE; + else if (buffer_space_left && + (bytes_in_last_packet == pipe->max_packet)) + transaction->stage = CVMX_USB_STAGE_NON_CONTROL; + else + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_OK); + } else { + if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) && + (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) && + (usbc_hcint.s.nak)) + pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING; + if (!buffer_space_left || + (bytes_in_last_packet < pipe->max_packet)) + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_OK); + } +} + +static void cvmx_usb_transfer_intr(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct cvmx_usb_transaction *transaction, + int buffer_space_left, + int bytes_in_last_packet) +{ + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + if (transaction->stage == CVMX_USB_STAGE_NON_CONTROL) { + transaction->stage = + CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE; + } else if (buffer_space_left && + (bytes_in_last_packet == pipe->max_packet)) { + transaction->stage = CVMX_USB_STAGE_NON_CONTROL; + } else { + pipe->next_tx_frame += pipe->interval; + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_OK); + } + } else if (!buffer_space_left || + (bytes_in_last_packet < pipe->max_packet)) { + pipe->next_tx_frame += pipe->interval; + cvmx_usb_complete(usb, pipe, transaction, CVMX_USB_STATUS_OK); + } +} + +static void cvmx_usb_transfer_isoc(struct octeon_hcd *usb, + struct cvmx_usb_pipe *pipe, + struct cvmx_usb_transaction *transaction, + int buffer_space_left, + int bytes_in_last_packet, + int bytes_this_transfer) +{ + if (cvmx_usb_pipe_needs_split(usb, pipe)) { + /* + * ISOCHRONOUS OUT splits don't require a complete split stage. + * Instead they use a sequence of begin OUT splits to transfer + * the data 188 bytes at a time. Once the transfer is complete, + * the pipe sleeps until the next schedule interval. + */ + if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) { + /* + * If no space left or this wasn't a max size packet + * then this transfer is complete. Otherwise start it + * again to send the next 188 bytes + */ + if (!buffer_space_left || (bytes_this_transfer < 188)) { + pipe->next_tx_frame += pipe->interval; + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_OK); + } + return; + } + if (transaction->stage == + CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE) { + /* + * We are in the incoming data phase. Keep getting data + * until we run out of space or get a small packet + */ + if ((buffer_space_left == 0) || + (bytes_in_last_packet < pipe->max_packet)) { + pipe->next_tx_frame += pipe->interval; + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_OK); + } + } else { + transaction->stage = + CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE; + } + } else { + pipe->next_tx_frame += pipe->interval; + cvmx_usb_complete(usb, pipe, transaction, CVMX_USB_STATUS_OK); + } +} + +/** + * Poll a channel for status + * + * @usb: USB device + * @channel: Channel to poll + * + * Returns: Zero on success + */ +static int cvmx_usb_poll_channel(struct octeon_hcd *usb, int channel) +{ + struct usb_hcd *hcd = octeon_to_hcd(usb); + struct device *dev = hcd->self.controller; + union cvmx_usbcx_hcintx usbc_hcint; + union cvmx_usbcx_hctsizx usbc_hctsiz; + union cvmx_usbcx_hccharx usbc_hcchar; + struct cvmx_usb_pipe *pipe; + struct cvmx_usb_transaction *transaction; + int bytes_this_transfer; + int bytes_in_last_packet; + int packets_processed; + int buffer_space_left; + + /* Read the interrupt status bits for the channel */ + usbc_hcint.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCINTX(channel, usb->index)); + + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) { + usbc_hcchar.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCCHARX(channel, usb->index)); + + if (usbc_hcchar.s.chena && usbc_hcchar.s.chdis) { + /* + * There seems to be a bug in CN31XX which can cause + * interrupt IN transfers to get stuck until we do a + * write of HCCHARX without changing things + */ + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCCHARX(channel, + usb->index), + usbc_hcchar.u32); + return 0; + } + + /* + * In non DMA mode the channels don't halt themselves. We need + * to manually disable channels that are left running + */ + if (!usbc_hcint.s.chhltd) { + if (usbc_hcchar.s.chena) { + union cvmx_usbcx_hcintmskx hcintmsk; + /* Disable all interrupts except CHHLTD */ + hcintmsk.u32 = 0; + hcintmsk.s.chhltdmsk = 1; + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCINTMSKX(channel, usb->index), + hcintmsk.u32); + usbc_hcchar.s.chdis = 1; + cvmx_usb_write_csr32(usb, + CVMX_USBCX_HCCHARX(channel, usb->index), + usbc_hcchar.u32); + return 0; + } else if (usbc_hcint.s.xfercompl) { + /* + * Successful IN/OUT with transfer complete. + * Channel halt isn't needed. + */ + } else { + dev_err(dev, "USB%d: Channel %d interrupt without halt\n", + usb->index, channel); + return 0; + } + } + } else { + /* + * There is are no interrupts that we need to process when the + * channel is still running + */ + if (!usbc_hcint.s.chhltd) + return 0; + } + + /* Disable the channel interrupts now that it is done */ + cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0); + usb->idle_hardware_channels |= (1 << channel); + + /* Make sure this channel is tied to a valid pipe */ + pipe = usb->pipe_for_channel[channel]; + prefetch(pipe); + if (!pipe) + return 0; + transaction = list_first_entry(&pipe->transactions, + typeof(*transaction), + node); + prefetch(transaction); + + /* + * Disconnect this pipe from the HW channel. Later the schedule + * function will figure out which pipe needs to go + */ + usb->pipe_for_channel[channel] = NULL; + pipe->flags &= ~CVMX_USB_PIPE_FLAGS_SCHEDULED; + + /* + * Read the channel config info so we can figure out how much data + * transferred + */ + usbc_hcchar.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCCHARX(channel, usb->index)); + usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HCTSIZX(channel, usb->index)); + + /* + * Calculating the number of bytes successfully transferred is dependent + * on the transfer direction + */ + packets_processed = transaction->pktcnt - usbc_hctsiz.s.pktcnt; + if (usbc_hcchar.s.epdir) { + /* + * IN transactions are easy. For every byte received the + * hardware decrements xfersize. All we need to do is subtract + * the current value of xfersize from its starting value and we + * know how many bytes were written to the buffer + */ + bytes_this_transfer = transaction->xfersize - + usbc_hctsiz.s.xfersize; + } else { + /* + * OUT transaction don't decrement xfersize. Instead pktcnt is + * decremented on every successful packet send. The hardware + * does this when it receives an ACK, or NYET. If it doesn't + * receive one of these responses pktcnt doesn't change + */ + bytes_this_transfer = packets_processed * usbc_hcchar.s.mps; + /* + * The last packet may not be a full transfer if we didn't have + * enough data + */ + if (bytes_this_transfer > transaction->xfersize) + bytes_this_transfer = transaction->xfersize; + } + /* Figure out how many bytes were in the last packet of the transfer */ + if (packets_processed) + bytes_in_last_packet = bytes_this_transfer - + (packets_processed - 1) * usbc_hcchar.s.mps; + else + bytes_in_last_packet = bytes_this_transfer; + + /* + * As a special case, setup transactions output the setup header, not + * the user's data. For this reason we don't count setup data as bytes + * transferred + */ + if ((transaction->stage == CVMX_USB_STAGE_SETUP) || + (transaction->stage == CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE)) + bytes_this_transfer = 0; + + /* + * Add the bytes transferred to the running total. It is important that + * bytes_this_transfer doesn't count any data that needs to be + * retransmitted + */ + transaction->actual_bytes += bytes_this_transfer; + if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS) + buffer_space_left = transaction->iso_packets[0].length - + transaction->actual_bytes; + else + buffer_space_left = transaction->buffer_length - + transaction->actual_bytes; + + /* + * We need to remember the PID toggle state for the next transaction. + * The hardware already updated it for the next transaction + */ + pipe->pid_toggle = !(usbc_hctsiz.s.pid == 0); + + /* + * For high speed bulk out, assume the next transaction will need to do + * a ping before proceeding. If this isn't true the ACK processing below + * will clear this flag + */ + if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) && + (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) && + (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)) + pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING; + + if (WARN_ON_ONCE(bytes_this_transfer < 0)) { + /* + * In some rare cases the DMA engine seems to get stuck and + * keeps substracting same byte count over and over again. In + * such case we just need to fail every transaction. + */ + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_ERROR); + return 0; + } + + if (usbc_hcint.s.stall) { + /* + * STALL as a response means this transaction cannot be + * completed because the device can't process transactions. Tell + * the user. Any data that was transferred will be counted on + * the actual bytes transferred + */ + pipe->pid_toggle = 0; + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_STALL); + } else if (usbc_hcint.s.xacterr) { + /* + * XactErr as a response means the device signaled + * something wrong with the transfer. For example, PID + * toggle errors cause these. + */ + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_XACTERR); + } else if (usbc_hcint.s.bblerr) { + /* Babble Error (BblErr) */ + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_BABBLEERR); + } else if (usbc_hcint.s.datatglerr) { + /* Data toggle error */ + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_DATATGLERR); + } else if (usbc_hcint.s.nyet) { + /* + * NYET as a response is only allowed in three cases: as a + * response to a ping, as a response to a split transaction, and + * as a response to a bulk out. The ping case is handled by + * hardware, so we only have splits and bulk out + */ + if (!cvmx_usb_pipe_needs_split(usb, pipe)) { + transaction->retries = 0; + /* + * If there is more data to go then we need to try + * again. Otherwise this transaction is complete + */ + if ((buffer_space_left == 0) || + (bytes_in_last_packet < pipe->max_packet)) + cvmx_usb_complete(usb, pipe, + transaction, + CVMX_USB_STATUS_OK); + } else { + /* + * Split transactions retry the split complete 4 times + * then rewind to the start split and do the entire + * transactions again + */ + transaction->retries++; + if ((transaction->retries & 0x3) == 0) { + /* + * Rewind to the beginning of the transaction by + * anding off the split complete bit + */ + transaction->stage &= ~1; + pipe->split_sc_frame = -1; + } + } + } else if (usbc_hcint.s.ack) { + transaction->retries = 0; + /* + * The ACK bit can only be checked after the other error bits. + * This is because a multi packet transfer may succeed in a + * number of packets and then get a different response on the + * last packet. In this case both ACK and the last response bit + * will be set. If none of the other response bits is set, then + * the last packet must have been an ACK + * + * Since we got an ACK, we know we don't need to do a ping on + * this pipe + */ + pipe->flags &= ~CVMX_USB_PIPE_FLAGS_NEED_PING; + + switch (transaction->type) { + case CVMX_USB_TRANSFER_CONTROL: + cvmx_usb_transfer_control(usb, pipe, transaction, + usbc_hcchar, + buffer_space_left, + bytes_in_last_packet); + break; + case CVMX_USB_TRANSFER_BULK: + cvmx_usb_transfer_bulk(usb, pipe, transaction, + usbc_hcint, buffer_space_left, + bytes_in_last_packet); + break; + case CVMX_USB_TRANSFER_INTERRUPT: + cvmx_usb_transfer_intr(usb, pipe, transaction, + buffer_space_left, + bytes_in_last_packet); + break; + case CVMX_USB_TRANSFER_ISOCHRONOUS: + cvmx_usb_transfer_isoc(usb, pipe, transaction, + buffer_space_left, + bytes_in_last_packet, + bytes_this_transfer); + break; + } + } else if (usbc_hcint.s.nak) { + /* + * If this was a split then clear our split in progress marker. + */ + if (usb->active_split == transaction) + usb->active_split = NULL; + /* + * NAK as a response means the device couldn't accept the + * transaction, but it should be retried in the future. Rewind + * to the beginning of the transaction by anding off the split + * complete bit. Retry in the next interval + */ + transaction->retries = 0; + transaction->stage &= ~1; + pipe->next_tx_frame += pipe->interval; + if (pipe->next_tx_frame < usb->frame_number) + pipe->next_tx_frame = usb->frame_number + + pipe->interval - + (usb->frame_number - pipe->next_tx_frame) % + pipe->interval; + } else { + struct cvmx_usb_port_status port; + + port = cvmx_usb_get_status(usb); + if (port.port_enabled) { + /* We'll retry the exact same transaction again */ + transaction->retries++; + } else { + /* + * We get channel halted interrupts with no result bits + * sets when the cable is unplugged + */ + cvmx_usb_complete(usb, pipe, transaction, + CVMX_USB_STATUS_ERROR); + } + } + return 0; +} + +static void octeon_usb_port_callback(struct octeon_hcd *usb) +{ + spin_unlock(&usb->lock); + usb_hcd_poll_rh_status(octeon_to_hcd(usb)); + spin_lock(&usb->lock); +} + +/** + * Poll the USB block for status and call all needed callback + * handlers. This function is meant to be called in the interrupt + * handler for the USB controller. It can also be called + * periodically in a loop for non-interrupt based operation. + * + * @usb: USB device state populated by cvmx_usb_initialize(). + * + * Returns: 0 or a negative error code. + */ +static int cvmx_usb_poll(struct octeon_hcd *usb) +{ + union cvmx_usbcx_hfnum usbc_hfnum; + union cvmx_usbcx_gintsts usbc_gintsts; + + prefetch_range(usb, sizeof(*usb)); + + /* Update the frame counter */ + usbc_hfnum.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index)); + if ((usb->frame_number & 0x3fff) > usbc_hfnum.s.frnum) + usb->frame_number += 0x4000; + usb->frame_number &= ~0x3fffull; + usb->frame_number |= usbc_hfnum.s.frnum; + + /* Read the pending interrupts */ + usbc_gintsts.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_GINTSTS(usb->index)); + + /* Clear the interrupts now that we know about them */ + cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index), + usbc_gintsts.u32); + + if (usbc_gintsts.s.rxflvl) { + /* + * RxFIFO Non-Empty (RxFLvl) + * Indicates that there is at least one packet pending to be + * read from the RxFIFO. + * + * In DMA mode this is handled by hardware + */ + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) + cvmx_usb_poll_rx_fifo(usb); + } + if (usbc_gintsts.s.ptxfemp || usbc_gintsts.s.nptxfemp) { + /* Fill the Tx FIFOs when not in DMA mode */ + if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) + cvmx_usb_poll_tx_fifo(usb); + } + if (usbc_gintsts.s.disconnint || usbc_gintsts.s.prtint) { + union cvmx_usbcx_hprt usbc_hprt; + /* + * Disconnect Detected Interrupt (DisconnInt) + * Asserted when a device disconnect is detected. + * + * Host Port Interrupt (PrtInt) + * The core sets this bit to indicate a change in port status of + * one of the O2P USB core ports in Host mode. The application + * must read the Host Port Control and Status (HPRT) register to + * determine the exact event that caused this interrupt. The + * application must clear the appropriate status bit in the Host + * Port Control and Status register to clear this bit. + * + * Call the user's port callback + */ + octeon_usb_port_callback(usb); + /* Clear the port change bits */ + usbc_hprt.u32 = + cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index)); + usbc_hprt.s.prtena = 0; + cvmx_usb_write_csr32(usb, CVMX_USBCX_HPRT(usb->index), + usbc_hprt.u32); + } + if (usbc_gintsts.s.hchint) { + /* + * Host Channels Interrupt (HChInt) + * The core sets this bit to indicate that an interrupt is + * pending on one of the channels of the core (in Host mode). + * The application must read the Host All Channels Interrupt + * (HAINT) register to determine the exact number of the channel + * on which the interrupt occurred, and then read the + * corresponding Host Channel-n Interrupt (HCINTn) register to + * determine the exact cause of the interrupt. The application + * must clear the appropriate status bit in the HCINTn register + * to clear this bit. + */ + union cvmx_usbcx_haint usbc_haint; + + usbc_haint.u32 = cvmx_usb_read_csr32(usb, + CVMX_USBCX_HAINT(usb->index)); + while (usbc_haint.u32) { + int channel; + + channel = __fls(usbc_haint.u32); + cvmx_usb_poll_channel(usb, channel); + usbc_haint.u32 ^= 1 << channel; + } + } + + cvmx_usb_schedule(usb, usbc_gintsts.s.sof); + + return 0; +} + +/* convert between an HCD pointer and the corresponding struct octeon_hcd */ +static inline struct octeon_hcd *hcd_to_octeon(struct usb_hcd *hcd) +{ + return (struct octeon_hcd *)(hcd->hcd_priv); +} + +static irqreturn_t octeon_usb_irq(struct usb_hcd *hcd) +{ + struct octeon_hcd *usb = hcd_to_octeon(hcd); + unsigned long flags; + + spin_lock_irqsave(&usb->lock, flags); + cvmx_usb_poll(usb); + spin_unlock_irqrestore(&usb->lock, flags); + return IRQ_HANDLED; +} + +static int octeon_usb_start(struct usb_hcd *hcd) +{ + hcd->state = HC_STATE_RUNNING; + return 0; +} + +static void octeon_usb_stop(struct usb_hcd *hcd) +{ + hcd->state = HC_STATE_HALT; +} + +static int octeon_usb_get_frame_number(struct usb_hcd *hcd) +{ + struct octeon_hcd *usb = hcd_to_octeon(hcd); + + return cvmx_usb_get_frame_number(usb); +} + +static int octeon_usb_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + struct octeon_hcd *usb = hcd_to_octeon(hcd); + struct device *dev = hcd->self.controller; + struct cvmx_usb_transaction *transaction = NULL; + struct cvmx_usb_pipe *pipe; + unsigned long flags; + struct cvmx_usb_iso_packet *iso_packet; + struct usb_host_endpoint *ep = urb->ep; + int rc; + + urb->status = 0; + spin_lock_irqsave(&usb->lock, flags); + + rc = usb_hcd_link_urb_to_ep(hcd, urb); + if (rc) { + spin_unlock_irqrestore(&usb->lock, flags); + return rc; + } + + if (!ep->hcpriv) { + enum cvmx_usb_transfer transfer_type; + enum cvmx_usb_speed speed; + int split_device = 0; + int split_port = 0; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + transfer_type = CVMX_USB_TRANSFER_ISOCHRONOUS; + break; + case PIPE_INTERRUPT: + transfer_type = CVMX_USB_TRANSFER_INTERRUPT; + break; + case PIPE_CONTROL: + transfer_type = CVMX_USB_TRANSFER_CONTROL; + break; + default: + transfer_type = CVMX_USB_TRANSFER_BULK; + break; + } + switch (urb->dev->speed) { + case USB_SPEED_LOW: + speed = CVMX_USB_SPEED_LOW; + break; + case USB_SPEED_FULL: + speed = CVMX_USB_SPEED_FULL; + break; + default: + speed = CVMX_USB_SPEED_HIGH; + break; + } + /* + * For slow devices on high speed ports we need to find the hub + * that does the speed translation so we know where to send the + * split transactions. + */ + if (speed != CVMX_USB_SPEED_HIGH) { + /* + * Start at this device and work our way up the usb + * tree. + */ + struct usb_device *dev = urb->dev; + + while (dev->parent) { + /* + * If our parent is high speed then he'll + * receive the splits. + */ + if (dev->parent->speed == USB_SPEED_HIGH) { + split_device = dev->parent->devnum; + split_port = dev->portnum; + break; + } + /* + * Move up the tree one level. If we make it all + * the way up the tree, then the port must not + * be in high speed mode and we don't need a + * split. + */ + dev = dev->parent; + } + } + pipe = cvmx_usb_open_pipe(usb, usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), speed, + le16_to_cpu(ep->desc.wMaxPacketSize) + & 0x7ff, + transfer_type, + usb_pipein(urb->pipe) ? + CVMX_USB_DIRECTION_IN : + CVMX_USB_DIRECTION_OUT, + urb->interval, + (le16_to_cpu(ep->desc.wMaxPacketSize) + >> 11) & 0x3, + split_device, split_port); + if (!pipe) { + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&usb->lock, flags); + dev_dbg(dev, "Failed to create pipe\n"); + return -ENOMEM; + } + ep->hcpriv = pipe; + } else { + pipe = ep->hcpriv; + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_ISOCHRONOUS: + dev_dbg(dev, "Submit isochronous to %d.%d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe)); + /* + * Allocate a structure to use for our private list of + * isochronous packets. + */ + iso_packet = kmalloc_array(urb->number_of_packets, + sizeof(struct cvmx_usb_iso_packet), + GFP_ATOMIC); + if (iso_packet) { + int i; + /* Fill the list with the data from the URB */ + for (i = 0; i < urb->number_of_packets; i++) { + iso_packet[i].offset = + urb->iso_frame_desc[i].offset; + iso_packet[i].length = + urb->iso_frame_desc[i].length; + iso_packet[i].status = CVMX_USB_STATUS_ERROR; + } + /* + * Store a pointer to the list in the URB setup_packet + * field. We know this currently isn't being used and + * this saves us a bunch of logic. + */ + urb->setup_packet = (char *)iso_packet; + transaction = cvmx_usb_submit_isochronous(usb, + pipe, urb); + /* + * If submit failed we need to free our private packet + * list. + */ + if (!transaction) { + urb->setup_packet = NULL; + kfree(iso_packet); + } + } + break; + case PIPE_INTERRUPT: + dev_dbg(dev, "Submit interrupt to %d.%d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe)); + transaction = cvmx_usb_submit_interrupt(usb, pipe, urb); + break; + case PIPE_CONTROL: + dev_dbg(dev, "Submit control to %d.%d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe)); + transaction = cvmx_usb_submit_control(usb, pipe, urb); + break; + case PIPE_BULK: + dev_dbg(dev, "Submit bulk to %d.%d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe)); + transaction = cvmx_usb_submit_bulk(usb, pipe, urb); + break; + } + if (!transaction) { + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&usb->lock, flags); + dev_dbg(dev, "Failed to submit\n"); + return -ENOMEM; + } + urb->hcpriv = transaction; + spin_unlock_irqrestore(&usb->lock, flags); + return 0; +} + +static int octeon_usb_urb_dequeue(struct usb_hcd *hcd, + struct urb *urb, + int status) +{ + struct octeon_hcd *usb = hcd_to_octeon(hcd); + unsigned long flags; + int rc; + + if (!urb->dev) + return -EINVAL; + + spin_lock_irqsave(&usb->lock, flags); + + rc = usb_hcd_check_unlink_urb(hcd, urb, status); + if (rc) + goto out; + + urb->status = status; + cvmx_usb_cancel(usb, urb->ep->hcpriv, urb->hcpriv); + +out: + spin_unlock_irqrestore(&usb->lock, flags); + + return rc; +} + +static void octeon_usb_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct device *dev = hcd->self.controller; + + if (ep->hcpriv) { + struct octeon_hcd *usb = hcd_to_octeon(hcd); + struct cvmx_usb_pipe *pipe = ep->hcpriv; + unsigned long flags; + + spin_lock_irqsave(&usb->lock, flags); + cvmx_usb_cancel_all(usb, pipe); + if (cvmx_usb_close_pipe(usb, pipe)) + dev_dbg(dev, "Closing pipe %p failed\n", pipe); + spin_unlock_irqrestore(&usb->lock, flags); + ep->hcpriv = NULL; + } +} + +static int octeon_usb_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct octeon_hcd *usb = hcd_to_octeon(hcd); + struct cvmx_usb_port_status port_status; + unsigned long flags; + + spin_lock_irqsave(&usb->lock, flags); + port_status = cvmx_usb_get_status(usb); + spin_unlock_irqrestore(&usb->lock, flags); + buf[0] = port_status.connect_change << 1; + + return buf[0] != 0; +} + +static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct octeon_hcd *usb = hcd_to_octeon(hcd); + struct device *dev = hcd->self.controller; + struct cvmx_usb_port_status usb_port_status; + int port_status; + struct usb_hub_descriptor *desc; + unsigned long flags; + + switch (typeReq) { + case ClearHubFeature: + dev_dbg(dev, "ClearHubFeature\n"); + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* Nothing required here */ + break; + default: + return -EINVAL; + } + break; + case ClearPortFeature: + dev_dbg(dev, "ClearPortFeature\n"); + if (wIndex != 1) { + dev_dbg(dev, " INVALID\n"); + return -EINVAL; + } + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + dev_dbg(dev, " ENABLE\n"); + spin_lock_irqsave(&usb->lock, flags); + cvmx_usb_disable(usb); + spin_unlock_irqrestore(&usb->lock, flags); + break; + case USB_PORT_FEAT_SUSPEND: + dev_dbg(dev, " SUSPEND\n"); + /* Not supported on Octeon */ + break; + case USB_PORT_FEAT_POWER: + dev_dbg(dev, " POWER\n"); + /* Not supported on Octeon */ + break; + case USB_PORT_FEAT_INDICATOR: + dev_dbg(dev, " INDICATOR\n"); + /* Port inidicator not supported */ + break; + case USB_PORT_FEAT_C_CONNECTION: + dev_dbg(dev, " C_CONNECTION\n"); + /* Clears drivers internal connect status change flag */ + spin_lock_irqsave(&usb->lock, flags); + usb->port_status = cvmx_usb_get_status(usb); + spin_unlock_irqrestore(&usb->lock, flags); + break; + case USB_PORT_FEAT_C_RESET: + dev_dbg(dev, " C_RESET\n"); + /* + * Clears the driver's internal Port Reset Change flag. + */ + spin_lock_irqsave(&usb->lock, flags); + usb->port_status = cvmx_usb_get_status(usb); + spin_unlock_irqrestore(&usb->lock, flags); + break; + case USB_PORT_FEAT_C_ENABLE: + dev_dbg(dev, " C_ENABLE\n"); + /* + * Clears the driver's internal Port Enable/Disable + * Change flag. + */ + spin_lock_irqsave(&usb->lock, flags); + usb->port_status = cvmx_usb_get_status(usb); + spin_unlock_irqrestore(&usb->lock, flags); + break; + case USB_PORT_FEAT_C_SUSPEND: + dev_dbg(dev, " C_SUSPEND\n"); + /* + * Clears the driver's internal Port Suspend Change + * flag, which is set when resume signaling on the host + * port is complete. + */ + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + dev_dbg(dev, " C_OVER_CURRENT\n"); + /* Clears the driver's overcurrent Change flag */ + spin_lock_irqsave(&usb->lock, flags); + usb->port_status = cvmx_usb_get_status(usb); + spin_unlock_irqrestore(&usb->lock, flags); + break; + default: + dev_dbg(dev, " UNKNOWN\n"); + return -EINVAL; + } + break; + case GetHubDescriptor: + dev_dbg(dev, "GetHubDescriptor\n"); + desc = (struct usb_hub_descriptor *)buf; + desc->bDescLength = 9; + desc->bDescriptorType = 0x29; + desc->bNbrPorts = 1; + desc->wHubCharacteristics = cpu_to_le16(0x08); + desc->bPwrOn2PwrGood = 1; + desc->bHubContrCurrent = 0; + desc->u.hs.DeviceRemovable[0] = 0; + desc->u.hs.DeviceRemovable[1] = 0xff; + break; + case GetHubStatus: + dev_dbg(dev, "GetHubStatus\n"); + *(__le32 *)buf = 0; + break; + case GetPortStatus: + dev_dbg(dev, "GetPortStatus\n"); + if (wIndex != 1) { + dev_dbg(dev, " INVALID\n"); + return -EINVAL; + } + + spin_lock_irqsave(&usb->lock, flags); + usb_port_status = cvmx_usb_get_status(usb); + spin_unlock_irqrestore(&usb->lock, flags); + port_status = 0; + + if (usb_port_status.connect_change) { + port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); + dev_dbg(dev, " C_CONNECTION\n"); + } + + if (usb_port_status.port_enabled) { + port_status |= (1 << USB_PORT_FEAT_C_ENABLE); + dev_dbg(dev, " C_ENABLE\n"); + } + + if (usb_port_status.connected) { + port_status |= (1 << USB_PORT_FEAT_CONNECTION); + dev_dbg(dev, " CONNECTION\n"); + } + + if (usb_port_status.port_enabled) { + port_status |= (1 << USB_PORT_FEAT_ENABLE); + dev_dbg(dev, " ENABLE\n"); + } + + if (usb_port_status.port_over_current) { + port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT); + dev_dbg(dev, " OVER_CURRENT\n"); + } + + if (usb_port_status.port_powered) { + port_status |= (1 << USB_PORT_FEAT_POWER); + dev_dbg(dev, " POWER\n"); + } + + if (usb_port_status.port_speed == CVMX_USB_SPEED_HIGH) { + port_status |= USB_PORT_STAT_HIGH_SPEED; + dev_dbg(dev, " HIGHSPEED\n"); + } else if (usb_port_status.port_speed == CVMX_USB_SPEED_LOW) { + port_status |= (1 << USB_PORT_FEAT_LOWSPEED); + dev_dbg(dev, " LOWSPEED\n"); + } + + *((__le32 *)buf) = cpu_to_le32(port_status); + break; + case SetHubFeature: + dev_dbg(dev, "SetHubFeature\n"); + /* No HUB features supported */ + break; + case SetPortFeature: + dev_dbg(dev, "SetPortFeature\n"); + if (wIndex != 1) { + dev_dbg(dev, " INVALID\n"); + return -EINVAL; + } + + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + dev_dbg(dev, " SUSPEND\n"); + return -EINVAL; + case USB_PORT_FEAT_POWER: + dev_dbg(dev, " POWER\n"); + /* + * Program the port power bit to drive VBUS on the USB. + */ + spin_lock_irqsave(&usb->lock, flags); + USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), + cvmx_usbcx_hprt, prtpwr, 1); + spin_unlock_irqrestore(&usb->lock, flags); + return 0; + case USB_PORT_FEAT_RESET: + dev_dbg(dev, " RESET\n"); + spin_lock_irqsave(&usb->lock, flags); + cvmx_usb_reset_port(usb); + spin_unlock_irqrestore(&usb->lock, flags); + return 0; + case USB_PORT_FEAT_INDICATOR: + dev_dbg(dev, " INDICATOR\n"); + /* Not supported */ + break; + default: + dev_dbg(dev, " UNKNOWN\n"); + return -EINVAL; + } + break; + default: + dev_dbg(dev, "Unknown root hub request\n"); + return -EINVAL; + } + return 0; +} + +static const struct hc_driver octeon_hc_driver = { + .description = "Octeon USB", + .product_desc = "Octeon Host Controller", + .hcd_priv_size = sizeof(struct octeon_hcd), + .irq = octeon_usb_irq, + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2, + .start = octeon_usb_start, + .stop = octeon_usb_stop, + .urb_enqueue = octeon_usb_urb_enqueue, + .urb_dequeue = octeon_usb_urb_dequeue, + .endpoint_disable = octeon_usb_endpoint_disable, + .get_frame_number = octeon_usb_get_frame_number, + .hub_status_data = octeon_usb_hub_status_data, + .hub_control = octeon_usb_hub_control, + .map_urb_for_dma = octeon_map_urb_for_dma, + .unmap_urb_for_dma = octeon_unmap_urb_for_dma, +}; + +static int octeon_usb_probe(struct platform_device *pdev) +{ + int status; + int initialize_flags; + int usb_num; + struct resource *res_mem; + struct device_node *usbn_node; + int irq = platform_get_irq(pdev, 0); + struct device *dev = &pdev->dev; + struct octeon_hcd *usb; + struct usb_hcd *hcd; + u32 clock_rate = 48000000; + bool is_crystal_clock = false; + const char *clock_type; + int i; + + if (!dev->of_node) { + dev_err(dev, "Error: empty of_node\n"); + return -ENXIO; + } + usbn_node = dev->of_node->parent; + + i = of_property_read_u32(usbn_node, + "clock-frequency", &clock_rate); + if (i) + i = of_property_read_u32(usbn_node, + "refclk-frequency", &clock_rate); + if (i) { + dev_err(dev, "No USBN \"clock-frequency\"\n"); + return -ENXIO; + } + switch (clock_rate) { + case 12000000: + initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ; + break; + case 24000000: + initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ; + break; + case 48000000: + initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ; + break; + default: + dev_err(dev, "Illegal USBN \"clock-frequency\" %u\n", + clock_rate); + return -ENXIO; + } + + i = of_property_read_string(usbn_node, + "cavium,refclk-type", &clock_type); + if (i) + i = of_property_read_string(usbn_node, + "refclk-type", &clock_type); + + if (!i && strcmp("crystal", clock_type) == 0) + is_crystal_clock = true; + + if (is_crystal_clock) + initialize_flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI; + else + initialize_flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) { + dev_err(dev, "found no memory resource\n"); + return -ENXIO; + } + usb_num = (res_mem->start >> 44) & 1; + + if (irq < 0) { + /* Defective device tree, but we know how to fix it. */ + irq_hw_number_t hwirq = usb_num ? (1 << 6) + 17 : 56; + + irq = irq_create_mapping(NULL, hwirq); + } + + /* + * Set the DMA mask to 64bits so we get buffers already translated for + * DMA. + */ + i = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (i) + return i; + + /* + * Only cn52XX and cn56XX have DWC_OTG USB hardware and the + * IOB priority registers. Under heavy network load USB + * hardware can be starved by the IOB causing a crash. Give + * it a priority boost if it has been waiting more than 400 + * cycles to avoid this situation. + * + * Testing indicates that a cnt_val of 8192 is not sufficient, + * but no failures are seen with 4096. We choose a value of + * 400 to give a safety factor of 10. + */ + if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN56XX)) { + union cvmx_iob_n2c_l2c_pri_cnt pri_cnt; + + pri_cnt.u64 = 0; + pri_cnt.s.cnt_enb = 1; + pri_cnt.s.cnt_val = 400; + cvmx_write_csr(CVMX_IOB_N2C_L2C_PRI_CNT, pri_cnt.u64); + } + + hcd = usb_create_hcd(&octeon_hc_driver, dev, dev_name(dev)); + if (!hcd) { + dev_dbg(dev, "Failed to allocate memory for HCD\n"); + return -1; + } + hcd->uses_new_polling = 1; + usb = (struct octeon_hcd *)hcd->hcd_priv; + + spin_lock_init(&usb->lock); + + usb->init_flags = initialize_flags; + + /* Initialize the USB state structure */ + usb->index = usb_num; + INIT_LIST_HEAD(&usb->idle_pipes); + for (i = 0; i < ARRAY_SIZE(usb->active_pipes); i++) + INIT_LIST_HEAD(&usb->active_pipes[i]); + + /* Due to an errata, CN31XX doesn't support DMA */ + if (OCTEON_IS_MODEL(OCTEON_CN31XX)) { + usb->init_flags |= CVMX_USB_INITIALIZE_FLAGS_NO_DMA; + /* Only use one channel with non DMA */ + usb->idle_hardware_channels = 0x1; + } else if (OCTEON_IS_MODEL(OCTEON_CN5XXX)) { + /* CN5XXX have an errata with channel 3 */ + usb->idle_hardware_channels = 0xf7; + } else { + usb->idle_hardware_channels = 0xff; + } + + status = cvmx_usb_initialize(dev, usb); + if (status) { + dev_dbg(dev, "USB initialization failed with %d\n", status); + usb_put_hcd(hcd); + return -1; + } + + status = usb_add_hcd(hcd, irq, 0); + if (status) { + dev_dbg(dev, "USB add HCD failed with %d\n", status); + usb_put_hcd(hcd); + return -1; + } + device_wakeup_enable(hcd->self.controller); + + dev_info(dev, "Registered HCD for port %d on irq %d\n", usb_num, irq); + + return 0; +} + +static int octeon_usb_remove(struct platform_device *pdev) +{ + int status; + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct octeon_hcd *usb = hcd_to_octeon(hcd); + unsigned long flags; + + usb_remove_hcd(hcd); + spin_lock_irqsave(&usb->lock, flags); + status = cvmx_usb_shutdown(usb); + spin_unlock_irqrestore(&usb->lock, flags); + if (status) + dev_dbg(dev, "USB shutdown failed with %d\n", status); + + usb_put_hcd(hcd); + + return 0; +} + +static const struct of_device_id octeon_usb_match[] = { + { + .compatible = "cavium,octeon-5750-usbc", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_usb_match); + +static struct platform_driver octeon_usb_driver = { + .driver = { + .name = "octeon-hcd", + .of_match_table = octeon_usb_match, + }, + .probe = octeon_usb_probe, + .remove = octeon_usb_remove, +}; + +static int __init octeon_usb_driver_init(void) +{ + if (usb_disabled()) + return 0; + + return platform_driver_register(&octeon_usb_driver); +} +module_init(octeon_usb_driver_init); + +static void __exit octeon_usb_driver_exit(void) +{ + if (usb_disabled()) + return; + + platform_driver_unregister(&octeon_usb_driver); +} +module_exit(octeon_usb_driver_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cavium, Inc. "); +MODULE_DESCRIPTION("Cavium Inc. OCTEON USB Host driver."); diff --git a/drivers/staging/octeon-usb/octeon-hcd.h b/drivers/staging/octeon-usb/octeon-hcd.h new file mode 100644 index 000000000000..9ed619c93a4e --- /dev/null +++ b/drivers/staging/octeon-usb/octeon-hcd.h @@ -0,0 +1,1847 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Octeon HCD hardware register definitions. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Some parts of the code were originally released under BSD license: + * + * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of Cavium Networks nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * This Software, including technical data, may be subject to U.S. export + * control laws, including the U.S. Export Administration Act and its associated + * regulations, and may be subject to export or import regulations in other + * countries. + * + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" + * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR + * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO + * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION + * OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM + * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, + * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF + * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR + * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR + * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. + */ + +#ifndef __OCTEON_HCD_H__ +#define __OCTEON_HCD_H__ + +#include + +#define CVMX_USBCXBASE 0x00016F0010000000ull +#define CVMX_USBCXREG1(reg, bid) \ + (CVMX_ADD_IO_SEG(CVMX_USBCXBASE | reg) + \ + ((bid) & 1) * 0x100000000000ull) +#define CVMX_USBCXREG2(reg, bid, off) \ + (CVMX_ADD_IO_SEG(CVMX_USBCXBASE | reg) + \ + (((off) & 7) + ((bid) & 1) * 0x8000000000ull) * 32) + +#define CVMX_USBCX_GAHBCFG(bid) CVMX_USBCXREG1(0x008, bid) +#define CVMX_USBCX_GHWCFG3(bid) CVMX_USBCXREG1(0x04c, bid) +#define CVMX_USBCX_GINTMSK(bid) CVMX_USBCXREG1(0x018, bid) +#define CVMX_USBCX_GINTSTS(bid) CVMX_USBCXREG1(0x014, bid) +#define CVMX_USBCX_GNPTXFSIZ(bid) CVMX_USBCXREG1(0x028, bid) +#define CVMX_USBCX_GNPTXSTS(bid) CVMX_USBCXREG1(0x02c, bid) +#define CVMX_USBCX_GOTGCTL(bid) CVMX_USBCXREG1(0x000, bid) +#define CVMX_USBCX_GRSTCTL(bid) CVMX_USBCXREG1(0x010, bid) +#define CVMX_USBCX_GRXFSIZ(bid) CVMX_USBCXREG1(0x024, bid) +#define CVMX_USBCX_GRXSTSPH(bid) CVMX_USBCXREG1(0x020, bid) +#define CVMX_USBCX_GUSBCFG(bid) CVMX_USBCXREG1(0x00c, bid) +#define CVMX_USBCX_HAINT(bid) CVMX_USBCXREG1(0x414, bid) +#define CVMX_USBCX_HAINTMSK(bid) CVMX_USBCXREG1(0x418, bid) +#define CVMX_USBCX_HCCHARX(off, bid) CVMX_USBCXREG2(0x500, bid, off) +#define CVMX_USBCX_HCFG(bid) CVMX_USBCXREG1(0x400, bid) +#define CVMX_USBCX_HCINTMSKX(off, bid) CVMX_USBCXREG2(0x50c, bid, off) +#define CVMX_USBCX_HCINTX(off, bid) CVMX_USBCXREG2(0x508, bid, off) +#define CVMX_USBCX_HCSPLTX(off, bid) CVMX_USBCXREG2(0x504, bid, off) +#define CVMX_USBCX_HCTSIZX(off, bid) CVMX_USBCXREG2(0x510, bid, off) +#define CVMX_USBCX_HFIR(bid) CVMX_USBCXREG1(0x404, bid) +#define CVMX_USBCX_HFNUM(bid) CVMX_USBCXREG1(0x408, bid) +#define CVMX_USBCX_HPRT(bid) CVMX_USBCXREG1(0x440, bid) +#define CVMX_USBCX_HPTXFSIZ(bid) CVMX_USBCXREG1(0x100, bid) +#define CVMX_USBCX_HPTXSTS(bid) CVMX_USBCXREG1(0x410, bid) + +#define CVMX_USBNXBID1(bid) (((bid) & 1) * 0x10000000ull) +#define CVMX_USBNXBID2(bid) (((bid) & 1) * 0x100000000000ull) + +#define CVMX_USBNXREG1(reg, bid) \ + (CVMX_ADD_IO_SEG(0x0001180068000000ull | reg) + CVMX_USBNXBID1(bid)) +#define CVMX_USBNXREG2(reg, bid) \ + (CVMX_ADD_IO_SEG(0x00016F0000000000ull | reg) + CVMX_USBNXBID2(bid)) + +#define CVMX_USBNX_CLK_CTL(bid) CVMX_USBNXREG1(0x10, bid) +#define CVMX_USBNX_DMA0_INB_CHN0(bid) CVMX_USBNXREG2(0x818, bid) +#define CVMX_USBNX_DMA0_OUTB_CHN0(bid) CVMX_USBNXREG2(0x858, bid) +#define CVMX_USBNX_USBP_CTL_STATUS(bid) CVMX_USBNXREG1(0x18, bid) + +/** + * cvmx_usbc#_gahbcfg + * + * Core AHB Configuration Register (GAHBCFG) + * + * This register can be used to configure the core after power-on or a change in + * mode of operation. This register mainly contains AHB system-related + * configuration parameters. The AHB is the processor interface to the O2P USB + * core. In general, software need not know about this interface except to + * program the values as specified. + * + * The application must program this register as part of the O2P USB core + * initialization. Do not change this register after the initial programming. + */ +union cvmx_usbcx_gahbcfg { + u32 u32; + /** + * struct cvmx_usbcx_gahbcfg_s + * @ptxfemplvl: Periodic TxFIFO Empty Level (PTxFEmpLvl) + * Software should set this bit to 0x1. + * Indicates when the Periodic TxFIFO Empty Interrupt bit in the + * Core Interrupt register (GINTSTS.PTxFEmp) is triggered. This + * bit is used only in Slave mode. + * * 1'b0: GINTSTS.PTxFEmp interrupt indicates that the Periodic + * TxFIFO is half empty + * * 1'b1: GINTSTS.PTxFEmp interrupt indicates that the Periodic + * TxFIFO is completely empty + * @nptxfemplvl: Non-Periodic TxFIFO Empty Level (NPTxFEmpLvl) + * Software should set this bit to 0x1. + * Indicates when the Non-Periodic TxFIFO Empty Interrupt bit in + * the Core Interrupt register (GINTSTS.NPTxFEmp) is triggered. + * This bit is used only in Slave mode. + * * 1'b0: GINTSTS.NPTxFEmp interrupt indicates that the Non- + * Periodic TxFIFO is half empty + * * 1'b1: GINTSTS.NPTxFEmp interrupt indicates that the Non- + * Periodic TxFIFO is completely empty + * @dmaen: DMA Enable (DMAEn) + * * 1'b0: Core operates in Slave mode + * * 1'b1: Core operates in a DMA mode + * @hbstlen: Burst Length/Type (HBstLen) + * This field has not effect and should be left as 0x0. + * @glblintrmsk: Global Interrupt Mask (GlblIntrMsk) + * Software should set this field to 0x1. + * The application uses this bit to mask or unmask the interrupt + * line assertion to itself. Irrespective of this bit's setting, + * the interrupt status registers are updated by the core. + * * 1'b0: Mask the interrupt assertion to the application. + * * 1'b1: Unmask the interrupt assertion to the application. + */ + struct cvmx_usbcx_gahbcfg_s { + __BITFIELD_FIELD(u32 reserved_9_31 : 23, + __BITFIELD_FIELD(u32 ptxfemplvl : 1, + __BITFIELD_FIELD(u32 nptxfemplvl : 1, + __BITFIELD_FIELD(u32 reserved_6_6 : 1, + __BITFIELD_FIELD(u32 dmaen : 1, + __BITFIELD_FIELD(u32 hbstlen : 4, + __BITFIELD_FIELD(u32 glblintrmsk : 1, + ;))))))) + } s; +}; + +/** + * cvmx_usbc#_ghwcfg3 + * + * User HW Config3 Register (GHWCFG3) + * + * This register contains the configuration options of the O2P USB core. + */ +union cvmx_usbcx_ghwcfg3 { + u32 u32; + /** + * struct cvmx_usbcx_ghwcfg3_s + * @dfifodepth: DFIFO Depth (DfifoDepth) + * This value is in terms of 32-bit words. + * * Minimum value is 32 + * * Maximum value is 32768 + * @ahbphysync: AHB and PHY Synchronous (AhbPhySync) + * Indicates whether AHB and PHY clocks are synchronous to + * each other. + * * 1'b0: No + * * 1'b1: Yes + * This bit is tied to 1. + * @rsttype: Reset Style for Clocked always Blocks in RTL (RstType) + * * 1'b0: Asynchronous reset is used in the core + * * 1'b1: Synchronous reset is used in the core + * @optfeature: Optional Features Removed (OptFeature) + * Indicates whether the User ID register, GPIO interface ports, + * and SOF toggle and counter ports were removed for gate count + * optimization. + * @vendor_control_interface_support: Vendor Control Interface Support + * * 1'b0: Vendor Control Interface is not available on the core. + * * 1'b1: Vendor Control Interface is available. + * @i2c_selection: I2C Selection + * * 1'b0: I2C Interface is not available on the core. + * * 1'b1: I2C Interface is available on the core. + * @otgen: OTG Function Enabled (OtgEn) + * The application uses this bit to indicate the O2P USB core's + * OTG capabilities. + * * 1'b0: Not OTG capable + * * 1'b1: OTG Capable + * @pktsizewidth: Width of Packet Size Counters (PktSizeWidth) + * * 3'b000: 4 bits + * * 3'b001: 5 bits + * * 3'b010: 6 bits + * * 3'b011: 7 bits + * * 3'b100: 8 bits + * * 3'b101: 9 bits + * * 3'b110: 10 bits + * * Others: Reserved + * @xfersizewidth: Width of Transfer Size Counters (XferSizeWidth) + * * 4'b0000: 11 bits + * * 4'b0001: 12 bits + * - ... + * * 4'b1000: 19 bits + * * Others: Reserved + */ + struct cvmx_usbcx_ghwcfg3_s { + __BITFIELD_FIELD(u32 dfifodepth : 16, + __BITFIELD_FIELD(u32 reserved_13_15 : 3, + __BITFIELD_FIELD(u32 ahbphysync : 1, + __BITFIELD_FIELD(u32 rsttype : 1, + __BITFIELD_FIELD(u32 optfeature : 1, + __BITFIELD_FIELD(u32 vendor_control_interface_support : 1, + __BITFIELD_FIELD(u32 i2c_selection : 1, + __BITFIELD_FIELD(u32 otgen : 1, + __BITFIELD_FIELD(u32 pktsizewidth : 3, + __BITFIELD_FIELD(u32 xfersizewidth : 4, + ;)))))))))) + } s; +}; + +/** + * cvmx_usbc#_gintmsk + * + * Core Interrupt Mask Register (GINTMSK) + * + * This register works with the Core Interrupt register to interrupt the + * application. When an interrupt bit is masked, the interrupt associated with + * that bit will not be generated. However, the Core Interrupt (GINTSTS) + * register bit corresponding to that interrupt will still be set. + * Mask interrupt: 1'b0, Unmask interrupt: 1'b1 + */ +union cvmx_usbcx_gintmsk { + u32 u32; + /** + * struct cvmx_usbcx_gintmsk_s + * @wkupintmsk: Resume/Remote Wakeup Detected Interrupt Mask + * (WkUpIntMsk) + * @sessreqintmsk: Session Request/New Session Detected Interrupt Mask + * (SessReqIntMsk) + * @disconnintmsk: Disconnect Detected Interrupt Mask (DisconnIntMsk) + * @conidstschngmsk: Connector ID Status Change Mask (ConIDStsChngMsk) + * @ptxfempmsk: Periodic TxFIFO Empty Mask (PTxFEmpMsk) + * @hchintmsk: Host Channels Interrupt Mask (HChIntMsk) + * @prtintmsk: Host Port Interrupt Mask (PrtIntMsk) + * @fetsuspmsk: Data Fetch Suspended Mask (FetSuspMsk) + * @incomplpmsk: Incomplete Periodic Transfer Mask (incomplPMsk) + * Incomplete Isochronous OUT Transfer Mask + * (incompISOOUTMsk) + * @incompisoinmsk: Incomplete Isochronous IN Transfer Mask + * (incompISOINMsk) + * @oepintmsk: OUT Endpoints Interrupt Mask (OEPIntMsk) + * @inepintmsk: IN Endpoints Interrupt Mask (INEPIntMsk) + * @epmismsk: Endpoint Mismatch Interrupt Mask (EPMisMsk) + * @eopfmsk: End of Periodic Frame Interrupt Mask (EOPFMsk) + * @isooutdropmsk: Isochronous OUT Packet Dropped Interrupt Mask + * (ISOOutDropMsk) + * @enumdonemsk: Enumeration Done Mask (EnumDoneMsk) + * @usbrstmsk: USB Reset Mask (USBRstMsk) + * @usbsuspmsk: USB Suspend Mask (USBSuspMsk) + * @erlysuspmsk: Early Suspend Mask (ErlySuspMsk) + * @i2cint: I2C Interrupt Mask (I2CINT) + * @ulpickintmsk: ULPI Carkit Interrupt Mask (ULPICKINTMsk) + * I2C Carkit Interrupt Mask (I2CCKINTMsk) + * @goutnakeffmsk: Global OUT NAK Effective Mask (GOUTNakEffMsk) + * @ginnakeffmsk: Global Non-Periodic IN NAK Effective Mask + * (GINNakEffMsk) + * @nptxfempmsk: Non-Periodic TxFIFO Empty Mask (NPTxFEmpMsk) + * @rxflvlmsk: Receive FIFO Non-Empty Mask (RxFLvlMsk) + * @sofmsk: Start of (micro)Frame Mask (SofMsk) + * @otgintmsk: OTG Interrupt Mask (OTGIntMsk) + * @modemismsk: Mode Mismatch Interrupt Mask (ModeMisMsk) + */ + struct cvmx_usbcx_gintmsk_s { + __BITFIELD_FIELD(u32 wkupintmsk : 1, + __BITFIELD_FIELD(u32 sessreqintmsk : 1, + __BITFIELD_FIELD(u32 disconnintmsk : 1, + __BITFIELD_FIELD(u32 conidstschngmsk : 1, + __BITFIELD_FIELD(u32 reserved_27_27 : 1, + __BITFIELD_FIELD(u32 ptxfempmsk : 1, + __BITFIELD_FIELD(u32 hchintmsk : 1, + __BITFIELD_FIELD(u32 prtintmsk : 1, + __BITFIELD_FIELD(u32 reserved_23_23 : 1, + __BITFIELD_FIELD(u32 fetsuspmsk : 1, + __BITFIELD_FIELD(u32 incomplpmsk : 1, + __BITFIELD_FIELD(u32 incompisoinmsk : 1, + __BITFIELD_FIELD(u32 oepintmsk : 1, + __BITFIELD_FIELD(u32 inepintmsk : 1, + __BITFIELD_FIELD(u32 epmismsk : 1, + __BITFIELD_FIELD(u32 reserved_16_16 : 1, + __BITFIELD_FIELD(u32 eopfmsk : 1, + __BITFIELD_FIELD(u32 isooutdropmsk : 1, + __BITFIELD_FIELD(u32 enumdonemsk : 1, + __BITFIELD_FIELD(u32 usbrstmsk : 1, + __BITFIELD_FIELD(u32 usbsuspmsk : 1, + __BITFIELD_FIELD(u32 erlysuspmsk : 1, + __BITFIELD_FIELD(u32 i2cint : 1, + __BITFIELD_FIELD(u32 ulpickintmsk : 1, + __BITFIELD_FIELD(u32 goutnakeffmsk : 1, + __BITFIELD_FIELD(u32 ginnakeffmsk : 1, + __BITFIELD_FIELD(u32 nptxfempmsk : 1, + __BITFIELD_FIELD(u32 rxflvlmsk : 1, + __BITFIELD_FIELD(u32 sofmsk : 1, + __BITFIELD_FIELD(u32 otgintmsk : 1, + __BITFIELD_FIELD(u32 modemismsk : 1, + __BITFIELD_FIELD(u32 reserved_0_0 : 1, + ;)))))))))))))))))))))))))))))))) + } s; +}; + +/** + * cvmx_usbc#_gintsts + * + * Core Interrupt Register (GINTSTS) + * + * This register interrupts the application for system-level events in the + * current mode of operation (Device mode or Host mode). It is shown in + * Interrupt. Some of the bits in this register are valid only in Host mode, + * while others are valid in Device mode only. This register also indicates the + * current mode of operation. In order to clear the interrupt status bits of + * type R_SS_WC, the application must write 1'b1 into the bit. The FIFO status + * interrupts are read only; once software reads from or writes to the FIFO + * while servicing these interrupts, FIFO interrupt conditions are cleared + * automatically. + */ +union cvmx_usbcx_gintsts { + u32 u32; + /** + * struct cvmx_usbcx_gintsts_s + * @wkupint: Resume/Remote Wakeup Detected Interrupt (WkUpInt) + * In Device mode, this interrupt is asserted when a resume is + * detected on the USB. In Host mode, this interrupt is asserted + * when a remote wakeup is detected on the USB. + * For more information on how to use this interrupt, see "Partial + * Power-Down and Clock Gating Programming Model" on + * page 353. + * @sessreqint: Session Request/New Session Detected Interrupt + * (SessReqInt) + * In Host mode, this interrupt is asserted when a session request + * is detected from the device. In Device mode, this interrupt is + * asserted when the utmiotg_bvalid signal goes high. + * For more information on how to use this interrupt, see "Partial + * Power-Down and Clock Gating Programming Model" on + * page 353. + * @disconnint: Disconnect Detected Interrupt (DisconnInt) + * Asserted when a device disconnect is detected. + * @conidstschng: Connector ID Status Change (ConIDStsChng) + * The core sets this bit when there is a change in connector ID + * status. + * @ptxfemp: Periodic TxFIFO Empty (PTxFEmp) + * Asserted when the Periodic Transmit FIFO is either half or + * completely empty and there is space for at least one entry to be + * written in the Periodic Request Queue. The half or completely + * empty status is determined by the Periodic TxFIFO Empty Level + * bit in the Core AHB Configuration register + * (GAHBCFG.PTxFEmpLvl). + * @hchint: Host Channels Interrupt (HChInt) + * The core sets this bit to indicate that an interrupt is pending + * on one of the channels of the core (in Host mode). The + * application must read the Host All Channels Interrupt (HAINT) + * register to determine the exact number of the channel on which + * the interrupt occurred, and then read the corresponding Host + * Channel-n Interrupt (HCINTn) register to determine the exact + * cause of the interrupt. The application must clear the + * appropriate status bit in the HCINTn register to clear this bit. + * @prtint: Host Port Interrupt (PrtInt) + * The core sets this bit to indicate a change in port status of + * one of the O2P USB core ports in Host mode. The application must + * read the Host Port Control and Status (HPRT) register to + * determine the exact event that caused this interrupt. The + * application must clear the appropriate status bit in the Host + * Port Control and Status register to clear this bit. + * @fetsusp: Data Fetch Suspended (FetSusp) + * This interrupt is valid only in DMA mode. This interrupt + * indicates that the core has stopped fetching data for IN + * endpoints due to the unavailability of TxFIFO space or Request + * Queue space. This interrupt is used by the application for an + * endpoint mismatch algorithm. + * @incomplp: Incomplete Periodic Transfer (incomplP) + * In Host mode, the core sets this interrupt bit when there are + * incomplete periodic transactions still pending which are + * scheduled for the current microframe. + * Incomplete Isochronous OUT Transfer (incompISOOUT) + * The Device mode, the core sets this interrupt to indicate that + * there is at least one isochronous OUT endpoint on which the + * transfer is not completed in the current microframe. This + * interrupt is asserted along with the End of Periodic Frame + * Interrupt (EOPF) bit in this register. + * @incompisoin: Incomplete Isochronous IN Transfer (incompISOIN) + * The core sets this interrupt to indicate that there is at least + * one isochronous IN endpoint on which the transfer is not + * completed in the current microframe. This interrupt is asserted + * along with the End of Periodic Frame Interrupt (EOPF) bit in + * this register. + * @oepint: OUT Endpoints Interrupt (OEPInt) + * The core sets this bit to indicate that an interrupt is pending + * on one of the OUT endpoints of the core (in Device mode). The + * application must read the Device All Endpoints Interrupt + * (DAINT) register to determine the exact number of the OUT + * endpoint on which the interrupt occurred, and then read the + * corresponding Device OUT Endpoint-n Interrupt (DOEPINTn) + * register to determine the exact cause of the interrupt. The + * application must clear the appropriate status bit in the + * corresponding DOEPINTn register to clear this bit. + * @iepint: IN Endpoints Interrupt (IEPInt) + * The core sets this bit to indicate that an interrupt is pending + * on one of the IN endpoints of the core (in Device mode). The + * application must read the Device All Endpoints Interrupt + * (DAINT) register to determine the exact number of the IN + * endpoint on which the interrupt occurred, and then read the + * corresponding Device IN Endpoint-n Interrupt (DIEPINTn) + * register to determine the exact cause of the interrupt. The + * application must clear the appropriate status bit in the + * corresponding DIEPINTn register to clear this bit. + * @epmis: Endpoint Mismatch Interrupt (EPMis) + * Indicates that an IN token has been received for a non-periodic + * endpoint, but the data for another endpoint is present in the + * top of the Non-Periodic Transmit FIFO and the IN endpoint + * mismatch count programmed by the application has expired. + * @eopf: End of Periodic Frame Interrupt (EOPF) + * Indicates that the period specified in the Periodic Frame + * Interval field of the Device Configuration register + * (DCFG.PerFrInt) has been reached in the current microframe. + * @isooutdrop: Isochronous OUT Packet Dropped Interrupt (ISOOutDrop) + * The core sets this bit when it fails to write an isochronous OUT + * packet into the RxFIFO because the RxFIFO doesn't have + * enough space to accommodate a maximum packet size packet + * for the isochronous OUT endpoint. + * @enumdone: Enumeration Done (EnumDone) + * The core sets this bit to indicate that speed enumeration is + * complete. The application must read the Device Status (DSTS) + * register to obtain the enumerated speed. + * @usbrst: USB Reset (USBRst) + * The core sets this bit to indicate that a reset is detected on + * the USB. + * @usbsusp: USB Suspend (USBSusp) + * The core sets this bit to indicate that a suspend was detected + * on the USB. The core enters the Suspended state when there + * is no activity on the phy_line_state_i signal for an extended + * period of time. + * @erlysusp: Early Suspend (ErlySusp) + * The core sets this bit to indicate that an Idle state has been + * detected on the USB for 3 ms. + * @i2cint: I2C Interrupt (I2CINT) + * This bit is always 0x0. + * @ulpickint: ULPI Carkit Interrupt (ULPICKINT) + * This bit is always 0x0. + * @goutnakeff: Global OUT NAK Effective (GOUTNakEff) + * Indicates that the Set Global OUT NAK bit in the Device Control + * register (DCTL.SGOUTNak), set by the application, has taken + * effect in the core. This bit can be cleared by writing the Clear + * Global OUT NAK bit in the Device Control register + * (DCTL.CGOUTNak). + * @ginnakeff: Global IN Non-Periodic NAK Effective (GINNakEff) + * Indicates that the Set Global Non-Periodic IN NAK bit in the + * Device Control register (DCTL.SGNPInNak), set by the + * application, has taken effect in the core. That is, the core has + * sampled the Global IN NAK bit set by the application. This bit + * can be cleared by clearing the Clear Global Non-Periodic IN + * NAK bit in the Device Control register (DCTL.CGNPInNak). + * This interrupt does not necessarily mean that a NAK handshake + * is sent out on the USB. The STALL bit takes precedence over + * the NAK bit. + * @nptxfemp: Non-Periodic TxFIFO Empty (NPTxFEmp) + * This interrupt is asserted when the Non-Periodic TxFIFO is + * either half or completely empty, and there is space for at least + * one entry to be written to the Non-Periodic Transmit Request + * Queue. The half or completely empty status is determined by + * the Non-Periodic TxFIFO Empty Level bit in the Core AHB + * Configuration register (GAHBCFG.NPTxFEmpLvl). + * @rxflvl: RxFIFO Non-Empty (RxFLvl) + * Indicates that there is at least one packet pending to be read + * from the RxFIFO. + * @sof: Start of (micro)Frame (Sof) + * In Host mode, the core sets this bit to indicate that an SOF + * (FS), micro-SOF (HS), or Keep-Alive (LS) is transmitted on the + * USB. The application must write a 1 to this bit to clear the + * interrupt. + * In Device mode, in the core sets this bit to indicate that an + * SOF token has been received on the USB. The application can read + * the Device Status register to get the current (micro)frame + * number. This interrupt is seen only when the core is operating + * at either HS or FS. + * @otgint: OTG Interrupt (OTGInt) + * The core sets this bit to indicate an OTG protocol event. The + * application must read the OTG Interrupt Status (GOTGINT) + * register to determine the exact event that caused this + * interrupt. The application must clear the appropriate status bit + * in the GOTGINT register to clear this bit. + * @modemis: Mode Mismatch Interrupt (ModeMis) + * The core sets this bit when the application is trying to access: + * * A Host mode register, when the core is operating in Device + * mode + * * A Device mode register, when the core is operating in Host + * mode + * The register access is completed on the AHB with an OKAY + * response, but is ignored by the core internally and doesn't + * affect the operation of the core. + * @curmod: Current Mode of Operation (CurMod) + * Indicates the current mode of operation. + * * 1'b0: Device mode + * * 1'b1: Host mode + */ + struct cvmx_usbcx_gintsts_s { + __BITFIELD_FIELD(u32 wkupint : 1, + __BITFIELD_FIELD(u32 sessreqint : 1, + __BITFIELD_FIELD(u32 disconnint : 1, + __BITFIELD_FIELD(u32 conidstschng : 1, + __BITFIELD_FIELD(u32 reserved_27_27 : 1, + __BITFIELD_FIELD(u32 ptxfemp : 1, + __BITFIELD_FIELD(u32 hchint : 1, + __BITFIELD_FIELD(u32 prtint : 1, + __BITFIELD_FIELD(u32 reserved_23_23 : 1, + __BITFIELD_FIELD(u32 fetsusp : 1, + __BITFIELD_FIELD(u32 incomplp : 1, + __BITFIELD_FIELD(u32 incompisoin : 1, + __BITFIELD_FIELD(u32 oepint : 1, + __BITFIELD_FIELD(u32 iepint : 1, + __BITFIELD_FIELD(u32 epmis : 1, + __BITFIELD_FIELD(u32 reserved_16_16 : 1, + __BITFIELD_FIELD(u32 eopf : 1, + __BITFIELD_FIELD(u32 isooutdrop : 1, + __BITFIELD_FIELD(u32 enumdone : 1, + __BITFIELD_FIELD(u32 usbrst : 1, + __BITFIELD_FIELD(u32 usbsusp : 1, + __BITFIELD_FIELD(u32 erlysusp : 1, + __BITFIELD_FIELD(u32 i2cint : 1, + __BITFIELD_FIELD(u32 ulpickint : 1, + __BITFIELD_FIELD(u32 goutnakeff : 1, + __BITFIELD_FIELD(u32 ginnakeff : 1, + __BITFIELD_FIELD(u32 nptxfemp : 1, + __BITFIELD_FIELD(u32 rxflvl : 1, + __BITFIELD_FIELD(u32 sof : 1, + __BITFIELD_FIELD(u32 otgint : 1, + __BITFIELD_FIELD(u32 modemis : 1, + __BITFIELD_FIELD(u32 curmod : 1, + ;)))))))))))))))))))))))))))))))) + } s; +}; + +/** + * cvmx_usbc#_gnptxfsiz + * + * Non-Periodic Transmit FIFO Size Register (GNPTXFSIZ) + * + * The application can program the RAM size and the memory start address for the + * Non-Periodic TxFIFO. + */ +union cvmx_usbcx_gnptxfsiz { + u32 u32; + /** + * struct cvmx_usbcx_gnptxfsiz_s + * @nptxfdep: Non-Periodic TxFIFO Depth (NPTxFDep) + * This value is in terms of 32-bit words. + * Minimum value is 16 + * Maximum value is 32768 + * @nptxfstaddr: Non-Periodic Transmit RAM Start Address (NPTxFStAddr) + * This field contains the memory start address for Non-Periodic + * Transmit FIFO RAM. + */ + struct cvmx_usbcx_gnptxfsiz_s { + __BITFIELD_FIELD(u32 nptxfdep : 16, + __BITFIELD_FIELD(u32 nptxfstaddr : 16, + ;)) + } s; +}; + +/** + * cvmx_usbc#_gnptxsts + * + * Non-Periodic Transmit FIFO/Queue Status Register (GNPTXSTS) + * + * This read-only register contains the free space information for the + * Non-Periodic TxFIFO and the Non-Periodic Transmit Request Queue. + */ +union cvmx_usbcx_gnptxsts { + u32 u32; + /** + * struct cvmx_usbcx_gnptxsts_s + * @nptxqtop: Top of the Non-Periodic Transmit Request Queue (NPTxQTop) + * Entry in the Non-Periodic Tx Request Queue that is currently + * being processed by the MAC. + * * Bits [30:27]: Channel/endpoint number + * * Bits [26:25]: + * - 2'b00: IN/OUT token + * - 2'b01: Zero-length transmit packet (device IN/host OUT) + * - 2'b10: PING/CSPLIT token + * - 2'b11: Channel halt command + * * Bit [24]: Terminate (last entry for selected channel/endpoint) + * @nptxqspcavail: Non-Periodic Transmit Request Queue Space Available + * (NPTxQSpcAvail) + * Indicates the amount of free space available in the Non- + * Periodic Transmit Request Queue. This queue holds both IN + * and OUT requests in Host mode. Device mode has only IN + * requests. + * * 8'h0: Non-Periodic Transmit Request Queue is full + * * 8'h1: 1 location available + * * 8'h2: 2 locations available + * * n: n locations available (0..8) + * * Others: Reserved + * @nptxfspcavail: Non-Periodic TxFIFO Space Avail (NPTxFSpcAvail) + * Indicates the amount of free space available in the Non- + * Periodic TxFIFO. + * Values are in terms of 32-bit words. + * * 16'h0: Non-Periodic TxFIFO is full + * * 16'h1: 1 word available + * * 16'h2: 2 words available + * * 16'hn: n words available (where 0..32768) + * * 16'h8000: 32768 words available + * * Others: Reserved + */ + struct cvmx_usbcx_gnptxsts_s { + __BITFIELD_FIELD(u32 reserved_31_31 : 1, + __BITFIELD_FIELD(u32 nptxqtop : 7, + __BITFIELD_FIELD(u32 nptxqspcavail : 8, + __BITFIELD_FIELD(u32 nptxfspcavail : 16, + ;)))) + } s; +}; + +/** + * cvmx_usbc#_grstctl + * + * Core Reset Register (GRSTCTL) + * + * The application uses this register to reset various hardware features inside + * the core. + */ +union cvmx_usbcx_grstctl { + u32 u32; + /** + * struct cvmx_usbcx_grstctl_s + * @ahbidle: AHB Master Idle (AHBIdle) + * Indicates that the AHB Master State Machine is in the IDLE + * condition. + * @dmareq: DMA Request Signal (DMAReq) + * Indicates that the DMA request is in progress. Used for debug. + * @txfnum: TxFIFO Number (TxFNum) + * This is the FIFO number that must be flushed using the TxFIFO + * Flush bit. This field must not be changed until the core clears + * the TxFIFO Flush bit. + * * 5'h0: Non-Periodic TxFIFO flush + * * 5'h1: Periodic TxFIFO 1 flush in Device mode or Periodic + * TxFIFO flush in Host mode + * * 5'h2: Periodic TxFIFO 2 flush in Device mode + * - ... + * * 5'hF: Periodic TxFIFO 15 flush in Device mode + * * 5'h10: Flush all the Periodic and Non-Periodic TxFIFOs in the + * core + * @txfflsh: TxFIFO Flush (TxFFlsh) + * This bit selectively flushes a single or all transmit FIFOs, but + * cannot do so if the core is in the midst of a transaction. + * The application must only write this bit after checking that the + * core is neither writing to the TxFIFO nor reading from the + * TxFIFO. + * The application must wait until the core clears this bit before + * performing any operations. This bit takes 8 clocks (of phy_clk + * or hclk, whichever is slower) to clear. + * @rxfflsh: RxFIFO Flush (RxFFlsh) + * The application can flush the entire RxFIFO using this bit, but + * must first ensure that the core is not in the middle of a + * transaction. + * The application must only write to this bit after checking that + * the core is neither reading from the RxFIFO nor writing to the + * RxFIFO. + * The application must wait until the bit is cleared before + * performing any other operations. This bit will take 8 clocks + * (slowest of PHY or AHB clock) to clear. + * @intknqflsh: IN Token Sequence Learning Queue Flush (INTknQFlsh) + * The application writes this bit to flush the IN Token Sequence + * Learning Queue. + * @frmcntrrst: Host Frame Counter Reset (FrmCntrRst) + * The application writes this bit to reset the (micro)frame number + * counter inside the core. When the (micro)frame counter is reset, + * the subsequent SOF sent out by the core will have a + * (micro)frame number of 0. + * @hsftrst: HClk Soft Reset (HSftRst) + * The application uses this bit to flush the control logic in the + * AHB Clock domain. Only AHB Clock Domain pipelines are reset. + * * FIFOs are not flushed with this bit. + * * All state machines in the AHB clock domain are reset to the + * Idle state after terminating the transactions on the AHB, + * following the protocol. + * * CSR control bits used by the AHB clock domain state + * machines are cleared. + * * To clear this interrupt, status mask bits that control the + * interrupt status and are generated by the AHB clock domain + * state machine are cleared. + * * Because interrupt status bits are not cleared, the application + * can get the status of any core events that occurred after it set + * this bit. + * This is a self-clearing bit that the core clears after all + * necessary logic is reset in the core. This may take several + * clocks, depending on the core's current state. + * @csftrst: Core Soft Reset (CSftRst) + * Resets the hclk and phy_clock domains as follows: + * * Clears the interrupts and all the CSR registers except the + * following register bits: + * - PCGCCTL.RstPdwnModule + * - PCGCCTL.GateHclk + * - PCGCCTL.PwrClmp + * - PCGCCTL.StopPPhyLPwrClkSelclk + * - GUSBCFG.PhyLPwrClkSel + * - GUSBCFG.DDRSel + * - GUSBCFG.PHYSel + * - GUSBCFG.FSIntf + * - GUSBCFG.ULPI_UTMI_Sel + * - GUSBCFG.PHYIf + * - HCFG.FSLSPclkSel + * - DCFG.DevSpd + * * All module state machines (except the AHB Slave Unit) are + * reset to the IDLE state, and all the transmit FIFOs and the + * receive FIFO are flushed. + * * Any transactions on the AHB Master are terminated as soon + * as possible, after gracefully completing the last data phase of + * an AHB transfer. Any transactions on the USB are terminated + * immediately. + * The application can write to this bit any time it wants to reset + * the core. This is a self-clearing bit and the core clears this + * bit after all the necessary logic is reset in the core, which + * may take several clocks, depending on the current state of the + * core. Once this bit is cleared software should wait at least 3 + * PHY clocks before doing any access to the PHY domain + * (synchronization delay). Software should also should check that + * bit 31 of this register is 1 (AHB Master is IDLE) before + * starting any operation. + * Typically software reset is used during software development + * and also when you dynamically change the PHY selection bits + * in the USB configuration registers listed above. When you + * change the PHY, the corresponding clock for the PHY is + * selected and used in the PHY domain. Once a new clock is + * selected, the PHY domain has to be reset for proper operation. + */ + struct cvmx_usbcx_grstctl_s { + __BITFIELD_FIELD(u32 ahbidle : 1, + __BITFIELD_FIELD(u32 dmareq : 1, + __BITFIELD_FIELD(u32 reserved_11_29 : 19, + __BITFIELD_FIELD(u32 txfnum : 5, + __BITFIELD_FIELD(u32 txfflsh : 1, + __BITFIELD_FIELD(u32 rxfflsh : 1, + __BITFIELD_FIELD(u32 intknqflsh : 1, + __BITFIELD_FIELD(u32 frmcntrrst : 1, + __BITFIELD_FIELD(u32 hsftrst : 1, + __BITFIELD_FIELD(u32 csftrst : 1, + ;)))))))))) + } s; +}; + +/** + * cvmx_usbc#_grxfsiz + * + * Receive FIFO Size Register (GRXFSIZ) + * + * The application can program the RAM size that must be allocated to the + * RxFIFO. + */ +union cvmx_usbcx_grxfsiz { + u32 u32; + /** + * struct cvmx_usbcx_grxfsiz_s + * @rxfdep: RxFIFO Depth (RxFDep) + * This value is in terms of 32-bit words. + * * Minimum value is 16 + * * Maximum value is 32768 + */ + struct cvmx_usbcx_grxfsiz_s { + __BITFIELD_FIELD(u32 reserved_16_31 : 16, + __BITFIELD_FIELD(u32 rxfdep : 16, + ;)) + } s; +}; + +/** + * cvmx_usbc#_grxstsph + * + * Receive Status Read and Pop Register, Host Mode (GRXSTSPH) + * + * A read to the Receive Status Read and Pop register returns and additionally + * pops the top data entry out of the RxFIFO. + * This Description is only valid when the core is in Host Mode. For Device Mode + * use USBC_GRXSTSPD instead. + * NOTE: GRXSTSPH and GRXSTSPD are physically the same register and share the + * same offset in the O2P USB core. The offset difference shown in this + * document is for software clarity and is actually ignored by the + * hardware. + */ +union cvmx_usbcx_grxstsph { + u32 u32; + /** + * struct cvmx_usbcx_grxstsph_s + * @pktsts: Packet Status (PktSts) + * Indicates the status of the received packet + * * 4'b0010: IN data packet received + * * 4'b0011: IN transfer completed (triggers an interrupt) + * * 4'b0101: Data toggle error (triggers an interrupt) + * * 4'b0111: Channel halted (triggers an interrupt) + * * Others: Reserved + * @dpid: Data PID (DPID) + * * 2'b00: DATA0 + * * 2'b10: DATA1 + * * 2'b01: DATA2 + * * 2'b11: MDATA + * @bcnt: Byte Count (BCnt) + * Indicates the byte count of the received IN data packet + * @chnum: Channel Number (ChNum) + * Indicates the channel number to which the current received + * packet belongs. + */ + struct cvmx_usbcx_grxstsph_s { + __BITFIELD_FIELD(u32 reserved_21_31 : 11, + __BITFIELD_FIELD(u32 pktsts : 4, + __BITFIELD_FIELD(u32 dpid : 2, + __BITFIELD_FIELD(u32 bcnt : 11, + __BITFIELD_FIELD(u32 chnum : 4, + ;))))) + } s; +}; + +/** + * cvmx_usbc#_gusbcfg + * + * Core USB Configuration Register (GUSBCFG) + * + * This register can be used to configure the core after power-on or a changing + * to Host mode or Device mode. It contains USB and USB-PHY related + * configuration parameters. The application must program this register before + * starting any transactions on either the AHB or the USB. Do not make changes + * to this register after the initial programming. + */ +union cvmx_usbcx_gusbcfg { + u32 u32; + /** + * struct cvmx_usbcx_gusbcfg_s + * @otgi2csel: UTMIFS or I2C Interface Select (OtgI2CSel) + * This bit is always 0x0. + * @phylpwrclksel: PHY Low-Power Clock Select (PhyLPwrClkSel) + * Software should set this bit to 0x0. + * Selects either 480-MHz or 48-MHz (low-power) PHY mode. In + * FS and LS modes, the PHY can usually operate on a 48-MHz + * clock to save power. + * * 1'b0: 480-MHz Internal PLL clock + * * 1'b1: 48-MHz External Clock + * In 480 MHz mode, the UTMI interface operates at either 60 or + * 30-MHz, depending upon whether 8- or 16-bit data width is + * selected. In 48-MHz mode, the UTMI interface operates at 48 + * MHz in FS mode and at either 48 or 6 MHz in LS mode + * (depending on the PHY vendor). + * This bit drives the utmi_fsls_low_power core output signal, and + * is valid only for UTMI+ PHYs. + * @usbtrdtim: USB Turnaround Time (USBTrdTim) + * Sets the turnaround time in PHY clocks. + * Specifies the response time for a MAC request to the Packet + * FIFO Controller (PFC) to fetch data from the DFIFO (SPRAM). + * This must be programmed to 0x5. + * @hnpcap: HNP-Capable (HNPCap) + * This bit is always 0x0. + * @srpcap: SRP-Capable (SRPCap) + * This bit is always 0x0. + * @ddrsel: ULPI DDR Select (DDRSel) + * Software should set this bit to 0x0. + * @physel: USB 2.0 High-Speed PHY or USB 1.1 Full-Speed Serial + * Software should set this bit to 0x0. + * @fsintf: Full-Speed Serial Interface Select (FSIntf) + * Software should set this bit to 0x0. + * @ulpi_utmi_sel: ULPI or UTMI+ Select (ULPI_UTMI_Sel) + * This bit is always 0x0. + * @phyif: PHY Interface (PHYIf) + * This bit is always 0x1. + * @toutcal: HS/FS Timeout Calibration (TOutCal) + * The number of PHY clocks that the application programs in this + * field is added to the high-speed/full-speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This may be required, since the delay + * introduced by the PHY in generating the linestate condition may + * vary from one PHY to another. + * The USB standard timeout value for high-speed operation is + * 736 to 816 (inclusive) bit times. The USB standard timeout + * value for full-speed operation is 16 to 18 (inclusive) bit + * times. The application must program this field based on the + * speed of enumeration. The number of bit times added per PHY + * clock are: + * High-speed operation: + * * One 30-MHz PHY clock = 16 bit times + * * One 60-MHz PHY clock = 8 bit times + * Full-speed operation: + * * One 30-MHz PHY clock = 0.4 bit times + * * One 60-MHz PHY clock = 0.2 bit times + * * One 48-MHz PHY clock = 0.25 bit times + */ + struct cvmx_usbcx_gusbcfg_s { + __BITFIELD_FIELD(u32 reserved_17_31 : 15, + __BITFIELD_FIELD(u32 otgi2csel : 1, + __BITFIELD_FIELD(u32 phylpwrclksel : 1, + __BITFIELD_FIELD(u32 reserved_14_14 : 1, + __BITFIELD_FIELD(u32 usbtrdtim : 4, + __BITFIELD_FIELD(u32 hnpcap : 1, + __BITFIELD_FIELD(u32 srpcap : 1, + __BITFIELD_FIELD(u32 ddrsel : 1, + __BITFIELD_FIELD(u32 physel : 1, + __BITFIELD_FIELD(u32 fsintf : 1, + __BITFIELD_FIELD(u32 ulpi_utmi_sel : 1, + __BITFIELD_FIELD(u32 phyif : 1, + __BITFIELD_FIELD(u32 toutcal : 3, + ;))))))))))))) + } s; +}; + +/** + * cvmx_usbc#_haint + * + * Host All Channels Interrupt Register (HAINT) + * + * When a significant event occurs on a channel, the Host All Channels Interrupt + * register interrupts the application using the Host Channels Interrupt bit of + * the Core Interrupt register (GINTSTS.HChInt). This is shown in Interrupt. + * There is one interrupt bit per channel, up to a maximum of 16 bits. Bits in + * this register are set and cleared when the application sets and clears bits + * in the corresponding Host Channel-n Interrupt register. + */ +union cvmx_usbcx_haint { + u32 u32; + /** + * struct cvmx_usbcx_haint_s + * @haint: Channel Interrupts (HAINT) + * One bit per channel: Bit 0 for Channel 0, bit 15 for Channel 15 + */ + struct cvmx_usbcx_haint_s { + __BITFIELD_FIELD(u32 reserved_16_31 : 16, + __BITFIELD_FIELD(u32 haint : 16, + ;)) + } s; +}; + +/** + * cvmx_usbc#_haintmsk + * + * Host All Channels Interrupt Mask Register (HAINTMSK) + * + * The Host All Channel Interrupt Mask register works with the Host All Channel + * Interrupt register to interrupt the application when an event occurs on a + * channel. There is one interrupt mask bit per channel, up to a maximum of 16 + * bits. + * Mask interrupt: 1'b0 Unmask interrupt: 1'b1 + */ +union cvmx_usbcx_haintmsk { + u32 u32; + /** + * struct cvmx_usbcx_haintmsk_s + * @haintmsk: Channel Interrupt Mask (HAINTMsk) + * One bit per channel: Bit 0 for channel 0, bit 15 for channel 15 + */ + struct cvmx_usbcx_haintmsk_s { + __BITFIELD_FIELD(u32 reserved_16_31 : 16, + __BITFIELD_FIELD(u32 haintmsk : 16, + ;)) + } s; +}; + +/** + * cvmx_usbc#_hcchar# + * + * Host Channel-n Characteristics Register (HCCHAR) + * + */ +union cvmx_usbcx_hccharx { + u32 u32; + /** + * struct cvmx_usbcx_hccharx_s + * @chena: Channel Enable (ChEna) + * This field is set by the application and cleared by the OTG + * host. + * * 1'b0: Channel disabled + * * 1'b1: Channel enabled + * @chdis: Channel Disable (ChDis) + * The application sets this bit to stop transmitting/receiving + * data on a channel, even before the transfer for that channel is + * complete. The application must wait for the Channel Disabled + * interrupt before treating the channel as disabled. + * @oddfrm: Odd Frame (OddFrm) + * This field is set (reset) by the application to indicate that + * the OTG host must perform a transfer in an odd (micro)frame. + * This field is applicable for only periodic (isochronous and + * interrupt) transactions. + * * 1'b0: Even (micro)frame + * * 1'b1: Odd (micro)frame + * @devaddr: Device Address (DevAddr) + * This field selects the specific device serving as the data + * source or sink. + * @ec: Multi Count (MC) / Error Count (EC) + * When the Split Enable bit of the Host Channel-n Split Control + * register (HCSPLTn.SpltEna) is reset (1'b0), this field indicates + * to the host the number of transactions that should be executed + * per microframe for this endpoint. + * * 2'b00: Reserved. This field yields undefined results. + * * 2'b01: 1 transaction + * * 2'b10: 2 transactions to be issued for this endpoint per + * microframe + * * 2'b11: 3 transactions to be issued for this endpoint per + * microframe + * When HCSPLTn.SpltEna is set (1'b1), this field indicates the + * number of immediate retries to be performed for a periodic split + * transactions on transaction errors. This field must be set to at + * least 2'b01. + * @eptype: Endpoint Type (EPType) + * Indicates the transfer type selected. + * * 2'b00: Control + * * 2'b01: Isochronous + * * 2'b10: Bulk + * * 2'b11: Interrupt + * @lspddev: Low-Speed Device (LSpdDev) + * This field is set by the application to indicate that this + * channel is communicating to a low-speed device. + * @epdir: Endpoint Direction (EPDir) + * Indicates whether the transaction is IN or OUT. + * * 1'b0: OUT + * * 1'b1: IN + * @epnum: Endpoint Number (EPNum) + * Indicates the endpoint number on the device serving as the + * data source or sink. + * @mps: Maximum Packet Size (MPS) + * Indicates the maximum packet size of the associated endpoint. + */ + struct cvmx_usbcx_hccharx_s { + __BITFIELD_FIELD(u32 chena : 1, + __BITFIELD_FIELD(u32 chdis : 1, + __BITFIELD_FIELD(u32 oddfrm : 1, + __BITFIELD_FIELD(u32 devaddr : 7, + __BITFIELD_FIELD(u32 ec : 2, + __BITFIELD_FIELD(u32 eptype : 2, + __BITFIELD_FIELD(u32 lspddev : 1, + __BITFIELD_FIELD(u32 reserved_16_16 : 1, + __BITFIELD_FIELD(u32 epdir : 1, + __BITFIELD_FIELD(u32 epnum : 4, + __BITFIELD_FIELD(u32 mps : 11, + ;))))))))))) + } s; +}; + +/** + * cvmx_usbc#_hcfg + * + * Host Configuration Register (HCFG) + * + * This register configures the core after power-on. Do not make changes to this + * register after initializing the host. + */ +union cvmx_usbcx_hcfg { + u32 u32; + /** + * struct cvmx_usbcx_hcfg_s + * @fslssupp: FS- and LS-Only Support (FSLSSupp) + * The application uses this bit to control the core's enumeration + * speed. Using this bit, the application can make the core + * enumerate as a FS host, even if the connected device supports + * HS traffic. Do not make changes to this field after initial + * programming. + * * 1'b0: HS/FS/LS, based on the maximum speed supported by + * the connected device + * * 1'b1: FS/LS-only, even if the connected device can support HS + * @fslspclksel: FS/LS PHY Clock Select (FSLSPclkSel) + * When the core is in FS Host mode + * * 2'b00: PHY clock is running at 30/60 MHz + * * 2'b01: PHY clock is running at 48 MHz + * * Others: Reserved + * When the core is in LS Host mode + * * 2'b00: PHY clock is running at 30/60 MHz. When the + * UTMI+/ULPI PHY Low Power mode is not selected, use + * 30/60 MHz. + * * 2'b01: PHY clock is running at 48 MHz. When the UTMI+ + * PHY Low Power mode is selected, use 48MHz if the PHY + * supplies a 48 MHz clock during LS mode. + * * 2'b10: PHY clock is running at 6 MHz. In USB 1.1 FS mode, + * use 6 MHz when the UTMI+ PHY Low Power mode is + * selected and the PHY supplies a 6 MHz clock during LS + * mode. If you select a 6 MHz clock during LS mode, you must + * do a soft reset. + * * 2'b11: Reserved + */ + struct cvmx_usbcx_hcfg_s { + __BITFIELD_FIELD(u32 reserved_3_31 : 29, + __BITFIELD_FIELD(u32 fslssupp : 1, + __BITFIELD_FIELD(u32 fslspclksel : 2, + ;))) + } s; +}; + +/** + * cvmx_usbc#_hcint# + * + * Host Channel-n Interrupt Register (HCINT) + * + * This register indicates the status of a channel with respect to USB- and + * AHB-related events. The application must read this register when the Host + * Channels Interrupt bit of the Core Interrupt register (GINTSTS.HChInt) is + * set. Before the application can read this register, it must first read + * the Host All Channels Interrupt (HAINT) register to get the exact channel + * number for the Host Channel-n Interrupt register. The application must clear + * the appropriate bit in this register to clear the corresponding bits in the + * HAINT and GINTSTS registers. + */ +union cvmx_usbcx_hcintx { + u32 u32; + /** + * struct cvmx_usbcx_hcintx_s + * @datatglerr: Data Toggle Error (DataTglErr) + * @frmovrun: Frame Overrun (FrmOvrun) + * @bblerr: Babble Error (BblErr) + * @xacterr: Transaction Error (XactErr) + * @nyet: NYET Response Received Interrupt (NYET) + * @ack: ACK Response Received Interrupt (ACK) + * @nak: NAK Response Received Interrupt (NAK) + * @stall: STALL Response Received Interrupt (STALL) + * @ahberr: This bit is always 0x0. + * @chhltd: Channel Halted (ChHltd) + * Indicates the transfer completed abnormally either because of + * any USB transaction error or in response to disable request by + * the application. + * @xfercompl: Transfer Completed (XferCompl) + * Transfer completed normally without any errors. + */ + struct cvmx_usbcx_hcintx_s { + __BITFIELD_FIELD(u32 reserved_11_31 : 21, + __BITFIELD_FIELD(u32 datatglerr : 1, + __BITFIELD_FIELD(u32 frmovrun : 1, + __BITFIELD_FIELD(u32 bblerr : 1, + __BITFIELD_FIELD(u32 xacterr : 1, + __BITFIELD_FIELD(u32 nyet : 1, + __BITFIELD_FIELD(u32 ack : 1, + __BITFIELD_FIELD(u32 nak : 1, + __BITFIELD_FIELD(u32 stall : 1, + __BITFIELD_FIELD(u32 ahberr : 1, + __BITFIELD_FIELD(u32 chhltd : 1, + __BITFIELD_FIELD(u32 xfercompl : 1, + ;)))))))))))) + } s; +}; + +/** + * cvmx_usbc#_hcintmsk# + * + * Host Channel-n Interrupt Mask Register (HCINTMSKn) + * + * This register reflects the mask for each channel status described in the + * previous section. + * Mask interrupt: 1'b0 Unmask interrupt: 1'b1 + */ +union cvmx_usbcx_hcintmskx { + u32 u32; + /** + * struct cvmx_usbcx_hcintmskx_s + * @datatglerrmsk: Data Toggle Error Mask (DataTglErrMsk) + * @frmovrunmsk: Frame Overrun Mask (FrmOvrunMsk) + * @bblerrmsk: Babble Error Mask (BblErrMsk) + * @xacterrmsk: Transaction Error Mask (XactErrMsk) + * @nyetmsk: NYET Response Received Interrupt Mask (NyetMsk) + * @ackmsk: ACK Response Received Interrupt Mask (AckMsk) + * @nakmsk: NAK Response Received Interrupt Mask (NakMsk) + * @stallmsk: STALL Response Received Interrupt Mask (StallMsk) + * @ahberrmsk: AHB Error Mask (AHBErrMsk) + * @chhltdmsk: Channel Halted Mask (ChHltdMsk) + * @xfercomplmsk: Transfer Completed Mask (XferComplMsk) + */ + struct cvmx_usbcx_hcintmskx_s { + __BITFIELD_FIELD(u32 reserved_11_31 : 21, + __BITFIELD_FIELD(u32 datatglerrmsk : 1, + __BITFIELD_FIELD(u32 frmovrunmsk : 1, + __BITFIELD_FIELD(u32 bblerrmsk : 1, + __BITFIELD_FIELD(u32 xacterrmsk : 1, + __BITFIELD_FIELD(u32 nyetmsk : 1, + __BITFIELD_FIELD(u32 ackmsk : 1, + __BITFIELD_FIELD(u32 nakmsk : 1, + __BITFIELD_FIELD(u32 stallmsk : 1, + __BITFIELD_FIELD(u32 ahberrmsk : 1, + __BITFIELD_FIELD(u32 chhltdmsk : 1, + __BITFIELD_FIELD(u32 xfercomplmsk : 1, + ;)))))))))))) + } s; +}; + +/** + * cvmx_usbc#_hcsplt# + * + * Host Channel-n Split Control Register (HCSPLT) + * + */ +union cvmx_usbcx_hcspltx { + u32 u32; + /** + * struct cvmx_usbcx_hcspltx_s + * @spltena: Split Enable (SpltEna) + * The application sets this field to indicate that this channel is + * enabled to perform split transactions. + * @compsplt: Do Complete Split (CompSplt) + * The application sets this field to request the OTG host to + * perform a complete split transaction. + * @xactpos: Transaction Position (XactPos) + * This field is used to determine whether to send all, first, + * middle, or last payloads with each OUT transaction. + * * 2'b11: All. This is the entire data payload is of this + * transaction (which is less than or equal to 188 bytes). + * * 2'b10: Begin. This is the first data payload of this + * transaction (which is larger than 188 bytes). + * * 2'b00: Mid. This is the middle payload of this transaction + * (which is larger than 188 bytes). + * * 2'b01: End. This is the last payload of this transaction + * (which is larger than 188 bytes). + * @hubaddr: Hub Address (HubAddr) + * This field holds the device address of the transaction + * translator's hub. + * @prtaddr: Port Address (PrtAddr) + * This field is the port number of the recipient transaction + * translator. + */ + struct cvmx_usbcx_hcspltx_s { + __BITFIELD_FIELD(u32 spltena : 1, + __BITFIELD_FIELD(u32 reserved_17_30 : 14, + __BITFIELD_FIELD(u32 compsplt : 1, + __BITFIELD_FIELD(u32 xactpos : 2, + __BITFIELD_FIELD(u32 hubaddr : 7, + __BITFIELD_FIELD(u32 prtaddr : 7, + ;)))))) + } s; +}; + +/** + * cvmx_usbc#_hctsiz# + * + * Host Channel-n Transfer Size Register (HCTSIZ) + * + */ +union cvmx_usbcx_hctsizx { + u32 u32; + /** + * struct cvmx_usbcx_hctsizx_s + * @dopng: Do Ping (DoPng) + * Setting this field to 1 directs the host to do PING protocol. + * @pid: PID (Pid) + * The application programs this field with the type of PID to use + * for the initial transaction. The host will maintain this field + * for the rest of the transfer. + * * 2'b00: DATA0 + * * 2'b01: DATA2 + * * 2'b10: DATA1 + * * 2'b11: MDATA (non-control)/SETUP (control) + * @pktcnt: Packet Count (PktCnt) + * This field is programmed by the application with the expected + * number of packets to be transmitted (OUT) or received (IN). + * The host decrements this count on every successful + * transmission or reception of an OUT/IN packet. Once this count + * reaches zero, the application is interrupted to indicate normal + * completion. + * @xfersize: Transfer Size (XferSize) + * For an OUT, this field is the number of data bytes the host will + * send during the transfer. + * For an IN, this field is the buffer size that the application + * has reserved for the transfer. The application is expected to + * program this field as an integer multiple of the maximum packet + * size for IN transactions (periodic and non-periodic). + */ + struct cvmx_usbcx_hctsizx_s { + __BITFIELD_FIELD(u32 dopng : 1, + __BITFIELD_FIELD(u32 pid : 2, + __BITFIELD_FIELD(u32 pktcnt : 10, + __BITFIELD_FIELD(u32 xfersize : 19, + ;)))) + } s; +}; + +/** + * cvmx_usbc#_hfir + * + * Host Frame Interval Register (HFIR) + * + * This register stores the frame interval information for the current speed to + * which the O2P USB core has enumerated. + */ +union cvmx_usbcx_hfir { + u32 u32; + /** + * struct cvmx_usbcx_hfir_s + * @frint: Frame Interval (FrInt) + * The value that the application programs to this field specifies + * the interval between two consecutive SOFs (FS) or micro- + * SOFs (HS) or Keep-Alive tokens (HS). This field contains the + * number of PHY clocks that constitute the required frame + * interval. The default value set in this field for a FS operation + * when the PHY clock frequency is 60 MHz. The application can + * write a value to this register only after the Port Enable bit of + * the Host Port Control and Status register (HPRT.PrtEnaPort) + * has been set. If no value is programmed, the core calculates + * the value based on the PHY clock specified in the FS/LS PHY + * Clock Select field of the Host Configuration register + * (HCFG.FSLSPclkSel). Do not change the value of this field + * after the initial configuration. + * * 125 us (PHY clock frequency for HS) + * * 1 ms (PHY clock frequency for FS/LS) + */ + struct cvmx_usbcx_hfir_s { + __BITFIELD_FIELD(u32 reserved_16_31 : 16, + __BITFIELD_FIELD(u32 frint : 16, + ;)) + } s; +}; + +/** + * cvmx_usbc#_hfnum + * + * Host Frame Number/Frame Time Remaining Register (HFNUM) + * + * This register indicates the current frame number. + * It also indicates the time remaining (in terms of the number of PHY clocks) + * in the current (micro)frame. + */ +union cvmx_usbcx_hfnum { + u32 u32; + /** + * struct cvmx_usbcx_hfnum_s + * @frrem: Frame Time Remaining (FrRem) + * Indicates the amount of time remaining in the current + * microframe (HS) or frame (FS/LS), in terms of PHY clocks. + * This field decrements on each PHY clock. When it reaches + * zero, this field is reloaded with the value in the Frame + * Interval register and a new SOF is transmitted on the USB. + * @frnum: Frame Number (FrNum) + * This field increments when a new SOF is transmitted on the + * USB, and is reset to 0 when it reaches 16'h3FFF. + */ + struct cvmx_usbcx_hfnum_s { + __BITFIELD_FIELD(u32 frrem : 16, + __BITFIELD_FIELD(u32 frnum : 16, + ;)) + } s; +}; + +/** + * cvmx_usbc#_hprt + * + * Host Port Control and Status Register (HPRT) + * + * This register is available in both Host and Device modes. + * Currently, the OTG Host supports only one port. + * A single register holds USB port-related information such as USB reset, + * enable, suspend, resume, connect status, and test mode for each port. The + * R_SS_WC bits in this register can trigger an interrupt to the application + * through the Host Port Interrupt bit of the Core Interrupt register + * (GINTSTS.PrtInt). On a Port Interrupt, the application must read this + * register and clear the bit that caused the interrupt. For the R_SS_WC bits, + * the application must write a 1 to the bit to clear the interrupt. + */ +union cvmx_usbcx_hprt { + u32 u32; + /** + * struct cvmx_usbcx_hprt_s + * @prtspd: Port Speed (PrtSpd) + * Indicates the speed of the device attached to this port. + * * 2'b00: High speed + * * 2'b01: Full speed + * * 2'b10: Low speed + * * 2'b11: Reserved + * @prttstctl: Port Test Control (PrtTstCtl) + * The application writes a nonzero value to this field to put + * the port into a Test mode, and the corresponding pattern is + * signaled on the port. + * * 4'b0000: Test mode disabled + * * 4'b0001: Test_J mode + * * 4'b0010: Test_K mode + * * 4'b0011: Test_SE0_NAK mode + * * 4'b0100: Test_Packet mode + * * 4'b0101: Test_Force_Enable + * * Others: Reserved + * PrtSpd must be zero (i.e. the interface must be in high-speed + * mode) to use the PrtTstCtl test modes. + * @prtpwr: Port Power (PrtPwr) + * The application uses this field to control power to this port, + * and the core clears this bit on an overcurrent condition. + * * 1'b0: Power off + * * 1'b1: Power on + * @prtlnsts: Port Line Status (PrtLnSts) + * Indicates the current logic level USB data lines + * * Bit [10]: Logic level of D- + * * Bit [11]: Logic level of D+ + * @prtrst: Port Reset (PrtRst) + * When the application sets this bit, a reset sequence is + * started on this port. The application must time the reset + * period and clear this bit after the reset sequence is + * complete. + * * 1'b0: Port not in reset + * * 1'b1: Port in reset + * The application must leave this bit set for at least a + * minimum duration mentioned below to start a reset on the + * port. The application can leave it set for another 10 ms in + * addition to the required minimum duration, before clearing + * the bit, even though there is no maximum limit set by the + * USB standard. + * * High speed: 50 ms + * * Full speed/Low speed: 10 ms + * @prtsusp: Port Suspend (PrtSusp) + * The application sets this bit to put this port in Suspend + * mode. The core only stops sending SOFs when this is set. + * To stop the PHY clock, the application must set the Port + * Clock Stop bit, which will assert the suspend input pin of + * the PHY. + * The read value of this bit reflects the current suspend + * status of the port. This bit is cleared by the core after a + * remote wakeup signal is detected or the application sets + * the Port Reset bit or Port Resume bit in this register or the + * Resume/Remote Wakeup Detected Interrupt bit or + * Disconnect Detected Interrupt bit in the Core Interrupt + * register (GINTSTS.WkUpInt or GINTSTS.DisconnInt, + * respectively). + * * 1'b0: Port not in Suspend mode + * * 1'b1: Port in Suspend mode + * @prtres: Port Resume (PrtRes) + * The application sets this bit to drive resume signaling on + * the port. The core continues to drive the resume signal + * until the application clears this bit. + * If the core detects a USB remote wakeup sequence, as + * indicated by the Port Resume/Remote Wakeup Detected + * Interrupt bit of the Core Interrupt register + * (GINTSTS.WkUpInt), the core starts driving resume + * signaling without application intervention and clears this bit + * when it detects a disconnect condition. The read value of + * this bit indicates whether the core is currently driving + * resume signaling. + * * 1'b0: No resume driven + * * 1'b1: Resume driven + * @prtovrcurrchng: Port Overcurrent Change (PrtOvrCurrChng) + * The core sets this bit when the status of the Port + * Overcurrent Active bit (bit 4) in this register changes. + * @prtovrcurract: Port Overcurrent Active (PrtOvrCurrAct) + * Indicates the overcurrent condition of the port. + * * 1'b0: No overcurrent condition + * * 1'b1: Overcurrent condition + * @prtenchng: Port Enable/Disable Change (PrtEnChng) + * The core sets this bit when the status of the Port Enable bit + * [2] of this register changes. + * @prtena: Port Enable (PrtEna) + * A port is enabled only by the core after a reset sequence, + * and is disabled by an overcurrent condition, a disconnect + * condition, or by the application clearing this bit. The + * application cannot set this bit by a register write. It can only + * clear it to disable the port. This bit does not trigger any + * interrupt to the application. + * * 1'b0: Port disabled + * * 1'b1: Port enabled + * @prtconndet: Port Connect Detected (PrtConnDet) + * The core sets this bit when a device connection is detected + * to trigger an interrupt to the application using the Host Port + * Interrupt bit of the Core Interrupt register (GINTSTS.PrtInt). + * The application must write a 1 to this bit to clear the + * interrupt. + * @prtconnsts: Port Connect Status (PrtConnSts) + * * 0: No device is attached to the port. + * * 1: A device is attached to the port. + */ + struct cvmx_usbcx_hprt_s { + __BITFIELD_FIELD(u32 reserved_19_31 : 13, + __BITFIELD_FIELD(u32 prtspd : 2, + __BITFIELD_FIELD(u32 prttstctl : 4, + __BITFIELD_FIELD(u32 prtpwr : 1, + __BITFIELD_FIELD(u32 prtlnsts : 2, + __BITFIELD_FIELD(u32 reserved_9_9 : 1, + __BITFIELD_FIELD(u32 prtrst : 1, + __BITFIELD_FIELD(u32 prtsusp : 1, + __BITFIELD_FIELD(u32 prtres : 1, + __BITFIELD_FIELD(u32 prtovrcurrchng : 1, + __BITFIELD_FIELD(u32 prtovrcurract : 1, + __BITFIELD_FIELD(u32 prtenchng : 1, + __BITFIELD_FIELD(u32 prtena : 1, + __BITFIELD_FIELD(u32 prtconndet : 1, + __BITFIELD_FIELD(u32 prtconnsts : 1, + ;))))))))))))))) + } s; +}; + +/** + * cvmx_usbc#_hptxfsiz + * + * Host Periodic Transmit FIFO Size Register (HPTXFSIZ) + * + * This register holds the size and the memory start address of the Periodic + * TxFIFO, as shown in Figures 310 and 311. + */ +union cvmx_usbcx_hptxfsiz { + u32 u32; + /** + * struct cvmx_usbcx_hptxfsiz_s + * @ptxfsize: Host Periodic TxFIFO Depth (PTxFSize) + * This value is in terms of 32-bit words. + * * Minimum value is 16 + * * Maximum value is 32768 + * @ptxfstaddr: Host Periodic TxFIFO Start Address (PTxFStAddr) + */ + struct cvmx_usbcx_hptxfsiz_s { + __BITFIELD_FIELD(u32 ptxfsize : 16, + __BITFIELD_FIELD(u32 ptxfstaddr : 16, + ;)) + } s; +}; + +/** + * cvmx_usbc#_hptxsts + * + * Host Periodic Transmit FIFO/Queue Status Register (HPTXSTS) + * + * This read-only register contains the free space information for the Periodic + * TxFIFO and the Periodic Transmit Request Queue + */ +union cvmx_usbcx_hptxsts { + u32 u32; + /** + * struct cvmx_usbcx_hptxsts_s + * @ptxqtop: Top of the Periodic Transmit Request Queue (PTxQTop) + * This indicates the entry in the Periodic Tx Request Queue that + * is currently being processes by the MAC. + * This register is used for debugging. + * * Bit [31]: Odd/Even (micro)frame + * - 1'b0: send in even (micro)frame + * - 1'b1: send in odd (micro)frame + * * Bits [30:27]: Channel/endpoint number + * * Bits [26:25]: Type + * - 2'b00: IN/OUT + * - 2'b01: Zero-length packet + * - 2'b10: CSPLIT + * - 2'b11: Disable channel command + * * Bit [24]: Terminate (last entry for the selected + * channel/endpoint) + * @ptxqspcavail: Periodic Transmit Request Queue Space Available + * (PTxQSpcAvail) + * Indicates the number of free locations available to be written + * in the Periodic Transmit Request Queue. This queue holds both + * IN and OUT requests. + * * 8'h0: Periodic Transmit Request Queue is full + * * 8'h1: 1 location available + * * 8'h2: 2 locations available + * * n: n locations available (0..8) + * * Others: Reserved + * @ptxfspcavail: Periodic Transmit Data FIFO Space Available + * (PTxFSpcAvail) + * Indicates the number of free locations available to be written + * to in the Periodic TxFIFO. + * Values are in terms of 32-bit words + * * 16'h0: Periodic TxFIFO is full + * * 16'h1: 1 word available + * * 16'h2: 2 words available + * * 16'hn: n words available (where 0..32768) + * * 16'h8000: 32768 words available + * * Others: Reserved + */ + struct cvmx_usbcx_hptxsts_s { + __BITFIELD_FIELD(u32 ptxqtop : 8, + __BITFIELD_FIELD(u32 ptxqspcavail : 8, + __BITFIELD_FIELD(u32 ptxfspcavail : 16, + ;))) + } s; +}; + +/** + * cvmx_usbn#_clk_ctl + * + * USBN_CLK_CTL = USBN's Clock Control + * + * This register is used to control the frequency of the hclk and the + * hreset and phy_rst signals. + */ +union cvmx_usbnx_clk_ctl { + u64 u64; + /** + * struct cvmx_usbnx_clk_ctl_s + * @divide2: The 'hclk' used by the USB subsystem is derived + * from the eclk. + * Also see the field DIVIDE. DIVIDE2<1> must currently + * be zero because it is not implemented, so the maximum + * ratio of eclk/hclk is currently 16. + * The actual divide number for hclk is: + * (DIVIDE2 + 1) * (DIVIDE + 1) + * @hclk_rst: When this field is '0' the HCLK-DIVIDER used to + * generate the hclk in the USB Subsystem is held + * in reset. This bit must be set to '0' before + * changing the value os DIVIDE in this register. + * The reset to the HCLK_DIVIDERis also asserted + * when core reset is asserted. + * @p_x_on: Force USB-PHY on during suspend. + * '1' USB-PHY XO block is powered-down during + * suspend. + * '0' USB-PHY XO block is powered-up during + * suspend. + * The value of this field must be set while POR is + * active. + * @p_rtype: PHY reference clock type + * On CN50XX/CN52XX/CN56XX the values are: + * '0' The USB-PHY uses a 12MHz crystal as a clock source + * at the USB_XO and USB_XI pins. + * '1' Reserved. + * '2' The USB_PHY uses 12/24/48MHz 2.5V board clock at the + * USB_XO pin. USB_XI should be tied to ground in this + * case. + * '3' Reserved. + * On CN3xxx bits 14 and 15 are p_xenbn and p_rclk and values are: + * '0' Reserved. + * '1' Reserved. + * '2' The PHY PLL uses the XO block output as a reference. + * The XO block uses an external clock supplied on the + * XO pin. USB_XI should be tied to ground for this + * usage. + * '3' The XO block uses the clock from a crystal. + * @p_com_on: '0' Force USB-PHY XO Bias, Bandgap and PLL to + * remain powered in Suspend Mode. + * '1' The USB-PHY XO Bias, Bandgap and PLL are + * powered down in suspend mode. + * The value of this field must be set while POR is + * active. + * @p_c_sel: Phy clock speed select. + * Selects the reference clock / crystal frequency. + * '11': Reserved + * '10': 48 MHz (reserved when a crystal is used) + * '01': 24 MHz (reserved when a crystal is used) + * '00': 12 MHz + * The value of this field must be set while POR is + * active. + * NOTE: if a crystal is used as a reference clock, + * this field must be set to 12 MHz. + * @cdiv_byp: Used to enable the bypass input to the USB_CLK_DIV. + * @sd_mode: Scaledown mode for the USBC. Control timing events + * in the USBC, for normal operation this must be '0'. + * @s_bist: Starts bist on the hclk memories, during the '0' + * to '1' transition. + * @por: Power On Reset for the PHY. + * Resets all the PHYS registers and state machines. + * @enable: When '1' allows the generation of the hclk. When + * '0' the hclk will not be generated. SEE DIVIDE + * field of this register. + * @prst: When this field is '0' the reset associated with + * the phy_clk functionality in the USB Subsystem is + * help in reset. This bit should not be set to '1' + * until the time it takes 6 clocks (hclk or phy_clk, + * whichever is slower) has passed. Under normal + * operation once this bit is set to '1' it should not + * be set to '0'. + * @hrst: When this field is '0' the reset associated with + * the hclk functioanlity in the USB Subsystem is + * held in reset.This bit should not be set to '1' + * until 12ms after phy_clk is stable. Under normal + * operation, once this bit is set to '1' it should + * not be set to '0'. + * @divide: The frequency of 'hclk' used by the USB subsystem + * is the eclk frequency divided by the value of + * (DIVIDE2 + 1) * (DIVIDE + 1), also see the field + * DIVIDE2 of this register. + * The hclk frequency should be less than 125Mhz. + * After writing a value to this field the SW should + * read the field for the value written. + * The ENABLE field of this register should not be set + * until AFTER this field is set and then read. + */ + struct cvmx_usbnx_clk_ctl_s { + __BITFIELD_FIELD(u64 reserved_20_63 : 44, + __BITFIELD_FIELD(u64 divide2 : 2, + __BITFIELD_FIELD(u64 hclk_rst : 1, + __BITFIELD_FIELD(u64 p_x_on : 1, + __BITFIELD_FIELD(u64 p_rtype : 2, + __BITFIELD_FIELD(u64 p_com_on : 1, + __BITFIELD_FIELD(u64 p_c_sel : 2, + __BITFIELD_FIELD(u64 cdiv_byp : 1, + __BITFIELD_FIELD(u64 sd_mode : 2, + __BITFIELD_FIELD(u64 s_bist : 1, + __BITFIELD_FIELD(u64 por : 1, + __BITFIELD_FIELD(u64 enable : 1, + __BITFIELD_FIELD(u64 prst : 1, + __BITFIELD_FIELD(u64 hrst : 1, + __BITFIELD_FIELD(u64 divide : 3, + ;))))))))))))))) + } s; +}; + +/** + * cvmx_usbn#_usbp_ctl_status + * + * USBN_USBP_CTL_STATUS = USBP Control And Status Register + * + * Contains general control and status information for the USBN block. + */ +union cvmx_usbnx_usbp_ctl_status { + u64 u64; + /** + * struct cvmx_usbnx_usbp_ctl_status_s + * @txrisetune: HS Transmitter Rise/Fall Time Adjustment + * @txvreftune: HS DC Voltage Level Adjustment + * @txfslstune: FS/LS Source Impedance Adjustment + * @txhsxvtune: Transmitter High-Speed Crossover Adjustment + * @sqrxtune: Squelch Threshold Adjustment + * @compdistune: Disconnect Threshold Adjustment + * @otgtune: VBUS Valid Threshold Adjustment + * @otgdisable: OTG Block Disable + * @portreset: Per_Port Reset + * @drvvbus: Drive VBUS + * @lsbist: Low-Speed BIST Enable. + * @fsbist: Full-Speed BIST Enable. + * @hsbist: High-Speed BIST Enable. + * @bist_done: PHY Bist Done. + * Asserted at the end of the PHY BIST sequence. + * @bist_err: PHY Bist Error. + * Indicates an internal error was detected during + * the BIST sequence. + * @tdata_out: PHY Test Data Out. + * Presents either internally generated signals or + * test register contents, based upon the value of + * test_data_out_sel. + * @siddq: Drives the USBP (USB-PHY) SIDDQ input. + * Normally should be set to zero. + * When customers have no intent to use USB PHY + * interface, they should: + * - still provide 3.3V to USB_VDD33, and + * - tie USB_REXT to 3.3V supply, and + * - set USBN*_USBP_CTL_STATUS[SIDDQ]=1 + * @txpreemphasistune: HS Transmitter Pre-Emphasis Enable + * @dma_bmode: When set to 1 the L2C DMA address will be updated + * with byte-counts between packets. When set to 0 + * the L2C DMA address is incremented to the next + * 4-byte aligned address after adding byte-count. + * @usbc_end: Bigendian input to the USB Core. This should be + * set to '0' for operation. + * @usbp_bist: PHY, This is cleared '0' to run BIST on the USBP. + * @tclk: PHY Test Clock, used to load TDATA_IN to the USBP. + * @dp_pulld: PHY DP_PULLDOWN input to the USB-PHY. + * This signal enables the pull-down resistance on + * the D+ line. '1' pull down-resistance is connected + * to D+/ '0' pull down resistance is not connected + * to D+. When an A/B device is acting as a host + * (downstream-facing port), dp_pulldown and + * dm_pulldown are enabled. This must not toggle + * during normal operation. + * @dm_pulld: PHY DM_PULLDOWN input to the USB-PHY. + * This signal enables the pull-down resistance on + * the D- line. '1' pull down-resistance is connected + * to D-. '0' pull down resistance is not connected + * to D-. When an A/B device is acting as a host + * (downstream-facing port), dp_pulldown and + * dm_pulldown are enabled. This must not toggle + * during normal operation. + * @hst_mode: When '0' the USB is acting as HOST, when '1' + * USB is acting as device. This field needs to be + * set while the USB is in reset. + * @tuning: Transmitter Tuning for High-Speed Operation. + * Tunes the current supply and rise/fall output + * times for high-speed operation. + * [20:19] == 11: Current supply increased + * approximately 9% + * [20:19] == 10: Current supply increased + * approximately 4.5% + * [20:19] == 01: Design default. + * [20:19] == 00: Current supply decreased + * approximately 4.5% + * [22:21] == 11: Rise and fall times are increased. + * [22:21] == 10: Design default. + * [22:21] == 01: Rise and fall times are decreased. + * [22:21] == 00: Rise and fall times are decreased + * further as compared to the 01 setting. + * @tx_bs_enh: Transmit Bit Stuffing on [15:8]. + * Enables or disables bit stuffing on data[15:8] + * when bit-stuffing is enabled. + * @tx_bs_en: Transmit Bit Stuffing on [7:0]. + * Enables or disables bit stuffing on data[7:0] + * when bit-stuffing is enabled. + * @loop_enb: PHY Loopback Test Enable. + * '1': During data transmission the receive is + * enabled. + * '0': During data transmission the receive is + * disabled. + * Must be '0' for normal operation. + * @vtest_enb: Analog Test Pin Enable. + * '1' The PHY's analog_test pin is enabled for the + * input and output of applicable analog test signals. + * '0' THe analog_test pin is disabled. + * @bist_enb: Built-In Self Test Enable. + * Used to activate BIST in the PHY. + * @tdata_sel: Test Data Out Select. + * '1' test_data_out[3:0] (PHY) register contents + * are output. '0' internally generated signals are + * output. + * @taddr_in: Mode Address for Test Interface. + * Specifies the register address for writing to or + * reading from the PHY test interface register. + * @tdata_in: Internal Testing Register Input Data and Select + * This is a test bus. Data is present on [3:0], + * and its corresponding select (enable) is present + * on bits [7:4]. + * @ate_reset: Reset input from automatic test equipment. + * This is a test signal. When the USB Core is + * powered up (not in Susned Mode), an automatic + * tester can use this to disable phy_clock and + * free_clk, then re-enable them with an aligned + * phase. + * '1': The phy_clk and free_clk outputs are + * disabled. "0": The phy_clock and free_clk outputs + * are available within a specific period after the + * de-assertion. + */ + struct cvmx_usbnx_usbp_ctl_status_s { + __BITFIELD_FIELD(u64 txrisetune : 1, + __BITFIELD_FIELD(u64 txvreftune : 4, + __BITFIELD_FIELD(u64 txfslstune : 4, + __BITFIELD_FIELD(u64 txhsxvtune : 2, + __BITFIELD_FIELD(u64 sqrxtune : 3, + __BITFIELD_FIELD(u64 compdistune : 3, + __BITFIELD_FIELD(u64 otgtune : 3, + __BITFIELD_FIELD(u64 otgdisable : 1, + __BITFIELD_FIELD(u64 portreset : 1, + __BITFIELD_FIELD(u64 drvvbus : 1, + __BITFIELD_FIELD(u64 lsbist : 1, + __BITFIELD_FIELD(u64 fsbist : 1, + __BITFIELD_FIELD(u64 hsbist : 1, + __BITFIELD_FIELD(u64 bist_done : 1, + __BITFIELD_FIELD(u64 bist_err : 1, + __BITFIELD_FIELD(u64 tdata_out : 4, + __BITFIELD_FIELD(u64 siddq : 1, + __BITFIELD_FIELD(u64 txpreemphasistune : 1, + __BITFIELD_FIELD(u64 dma_bmode : 1, + __BITFIELD_FIELD(u64 usbc_end : 1, + __BITFIELD_FIELD(u64 usbp_bist : 1, + __BITFIELD_FIELD(u64 tclk : 1, + __BITFIELD_FIELD(u64 dp_pulld : 1, + __BITFIELD_FIELD(u64 dm_pulld : 1, + __BITFIELD_FIELD(u64 hst_mode : 1, + __BITFIELD_FIELD(u64 tuning : 4, + __BITFIELD_FIELD(u64 tx_bs_enh : 1, + __BITFIELD_FIELD(u64 tx_bs_en : 1, + __BITFIELD_FIELD(u64 loop_enb : 1, + __BITFIELD_FIELD(u64 vtest_enb : 1, + __BITFIELD_FIELD(u64 bist_enb : 1, + __BITFIELD_FIELD(u64 tdata_sel : 1, + __BITFIELD_FIELD(u64 taddr_in : 4, + __BITFIELD_FIELD(u64 tdata_in : 8, + __BITFIELD_FIELD(u64 ate_reset : 1, + ;))))))))))))))))))))))))))))))))))) + } s; +}; + +#endif /* __OCTEON_HCD_H__ */ From 422d97b8b05ed38cc5f67522ddb821868ea272a7 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 5 Feb 2020 13:11:12 +1300 Subject: [PATCH 0155/2231] Revert "staging: octeon: delete driver" This reverts commit 710d7fbe21ee2ceab121f1f84a20edf68f9f9742. Re-instate the code so subsequent commits can clean it up and get it building properly. Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20200205001116.14096-3-chris.packham@alliedtelesis.co.nz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/octeon/Kconfig | 16 + drivers/staging/octeon/Makefile | 19 + drivers/staging/octeon/TODO | 9 + drivers/staging/octeon/ethernet-defines.h | 40 + drivers/staging/octeon/ethernet-mdio.c | 178 +++ drivers/staging/octeon/ethernet-mdio.h | 28 + drivers/staging/octeon/ethernet-mem.c | 154 +++ drivers/staging/octeon/ethernet-mem.h | 9 + drivers/staging/octeon/ethernet-rgmii.c | 158 +++ drivers/staging/octeon/ethernet-rx.c | 538 ++++++++ drivers/staging/octeon/ethernet-rx.h | 31 + drivers/staging/octeon/ethernet-sgmii.c | 30 + drivers/staging/octeon/ethernet-spi.c | 226 ++++ drivers/staging/octeon/ethernet-tx.c | 717 +++++++++++ drivers/staging/octeon/ethernet-tx.h | 14 + drivers/staging/octeon/ethernet-util.h | 47 + drivers/staging/octeon/ethernet.c | 992 ++++++++++++++ drivers/staging/octeon/octeon-ethernet.h | 107 ++ drivers/staging/octeon/octeon-stubs.h | 1433 +++++++++++++++++++++ 21 files changed, 4749 insertions(+) create mode 100644 drivers/staging/octeon/Kconfig create mode 100644 drivers/staging/octeon/Makefile create mode 100644 drivers/staging/octeon/TODO create mode 100644 drivers/staging/octeon/ethernet-defines.h create mode 100644 drivers/staging/octeon/ethernet-mdio.c create mode 100644 drivers/staging/octeon/ethernet-mdio.h create mode 100644 drivers/staging/octeon/ethernet-mem.c create mode 100644 drivers/staging/octeon/ethernet-mem.h create mode 100644 drivers/staging/octeon/ethernet-rgmii.c create mode 100644 drivers/staging/octeon/ethernet-rx.c create mode 100644 drivers/staging/octeon/ethernet-rx.h create mode 100644 drivers/staging/octeon/ethernet-sgmii.c create mode 100644 drivers/staging/octeon/ethernet-spi.c create mode 100644 drivers/staging/octeon/ethernet-tx.c create mode 100644 drivers/staging/octeon/ethernet-tx.h create mode 100644 drivers/staging/octeon/ethernet-util.h create mode 100644 drivers/staging/octeon/ethernet.c create mode 100644 drivers/staging/octeon/octeon-ethernet.h create mode 100644 drivers/staging/octeon/octeon-stubs.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 9ec982d16e1c..f46c1f7d0d39 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -42,6 +42,8 @@ source "drivers/staging/rtl8188eu/Kconfig" source "drivers/staging/rts5208/Kconfig" +source "drivers/staging/octeon/Kconfig" + source "drivers/staging/octeon-usb/Kconfig" source "drivers/staging/vt6655/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index be3f0a814bf2..bb26d753066c 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_R8712U) += rtl8712/ obj-$(CONFIG_R8188EU) += rtl8188eu/ obj-$(CONFIG_RTS5208) += rts5208/ obj-$(CONFIG_NETLOGIC_XLR_NET) += netlogic/ +obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_OCTEON_USB) += octeon-usb/ obj-$(CONFIG_VT6655) += vt6655/ obj-$(CONFIG_VT6656) += vt6656/ diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig new file mode 100644 index 000000000000..e7f4ddcc1361 --- /dev/null +++ b/drivers/staging/octeon/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +config OCTEON_ETHERNET + tristate "Cavium Networks Octeon Ethernet support" + depends on CAVIUM_OCTEON_SOC || COMPILE_TEST + depends on NETDEVICES + depends on BROKEN + select PHYLIB + select MDIO_OCTEON + help + This driver supports the builtin ethernet ports on Cavium + Networks' products in the Octeon family. This driver supports the + CN3XXX and CN5XXX Octeon processors. + + To compile this driver as a module, choose M here. The module + will be called octeon-ethernet. + diff --git a/drivers/staging/octeon/Makefile b/drivers/staging/octeon/Makefile new file mode 100644 index 000000000000..3887cf5f1e84 --- /dev/null +++ b/drivers/staging/octeon/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2005-2009 Cavium Networks +# + +# +# Makefile for Cavium OCTEON on-board ethernet driver +# + +obj-${CONFIG_OCTEON_ETHERNET} := octeon-ethernet.o + +octeon-ethernet-y := ethernet.o +octeon-ethernet-y += ethernet-mdio.o +octeon-ethernet-y += ethernet-mem.o +octeon-ethernet-y += ethernet-rgmii.o +octeon-ethernet-y += ethernet-rx.o +octeon-ethernet-y += ethernet-sgmii.o +octeon-ethernet-y += ethernet-spi.o +octeon-ethernet-y += ethernet-tx.o diff --git a/drivers/staging/octeon/TODO b/drivers/staging/octeon/TODO new file mode 100644 index 000000000000..67a0a1f6b922 --- /dev/null +++ b/drivers/staging/octeon/TODO @@ -0,0 +1,9 @@ +This driver is functional and supports Ethernet on OCTEON+/OCTEON2/OCTEON3 +chips at least up to CN7030. + +TODO: + - general code review and clean up + - make driver self-contained instead of being split between staging and + arch/mips/cavium-octeon. + +Contact: Aaro Koskinen diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h new file mode 100644 index 000000000000..ef9e767b0e2e --- /dev/null +++ b/drivers/staging/octeon/ethernet-defines.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +/* + * A few defines are used to control the operation of this driver: + * USE_ASYNC_IOBDMA + * Use asynchronous IO access to hardware. This uses Octeon's asynchronous + * IOBDMAs to issue IO accesses without stalling. Set this to zero + * to disable this. Note that IOBDMAs require CVMSEG. + * REUSE_SKBUFFS_WITHOUT_FREE + * Allows the TX path to free an skbuff into the FPA hardware pool. This + * can significantly improve performance for forwarding and bridging, but + * may be somewhat dangerous. Checks are made, but if any buffer is reused + * without the proper Linux cleanup, the networking stack may have very + * bizarre bugs. + */ +#ifndef __ETHERNET_DEFINES_H__ +#define __ETHERNET_DEFINES_H__ + +#ifdef CONFIG_NETFILTER +#define REUSE_SKBUFFS_WITHOUT_FREE 0 +#else +#define REUSE_SKBUFFS_WITHOUT_FREE 1 +#endif + +#define USE_ASYNC_IOBDMA (CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0) + +/* Maximum number of SKBs to try to free per xmit packet. */ +#define MAX_OUT_QUEUE_DEPTH 1000 + +#define FAU_TOTAL_TX_TO_CLEAN (CVMX_FAU_REG_END - sizeof(u32)) +#define FAU_NUM_PACKET_BUFFERS_TO_FREE (FAU_TOTAL_TX_TO_CLEAN - sizeof(u32)) + +#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS + 1) + +#endif /* __ETHERNET_DEFINES_H__ */ diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c new file mode 100644 index 000000000000..c798672d61b2 --- /dev/null +++ b/drivers/staging/octeon/ethernet-mdio.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "octeon-ethernet.h" +#include "ethernet-defines.h" +#include "ethernet-mdio.h" +#include "ethernet-util.h" + +static void cvm_oct_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strlcpy(info->version, UTS_RELEASE, sizeof(info->version)); + strlcpy(info->bus_info, "Builtin", sizeof(info->bus_info)); +} + +static int cvm_oct_nway_reset(struct net_device *dev) +{ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (dev->phydev) + return phy_start_aneg(dev->phydev); + + return -EINVAL; +} + +const struct ethtool_ops cvm_oct_ethtool_ops = { + .get_drvinfo = cvm_oct_get_drvinfo, + .nway_reset = cvm_oct_nway_reset, + .get_link = ethtool_op_get_link, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, +}; + +/** + * cvm_oct_ioctl - IOCTL support for PHY control + * @dev: Device to change + * @rq: the request + * @cmd: the command + * + * Returns Zero on success + */ +int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + if (!netif_running(dev)) + return -EINVAL; + + if (!dev->phydev) + return -EINVAL; + + return phy_mii_ioctl(dev->phydev, rq, cmd); +} + +void cvm_oct_note_carrier(struct octeon_ethernet *priv, + union cvmx_helper_link_info li) +{ + if (li.s.link_up) { + pr_notice_ratelimited("%s: %u Mbps %s duplex, port %d, queue %d\n", + netdev_name(priv->netdev), li.s.speed, + (li.s.full_duplex) ? "Full" : "Half", + priv->port, priv->queue); + } else { + pr_notice_ratelimited("%s: Link down\n", + netdev_name(priv->netdev)); + } +} + +void cvm_oct_adjust_link(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + union cvmx_helper_link_info link_info; + + link_info.u64 = 0; + link_info.s.link_up = dev->phydev->link ? 1 : 0; + link_info.s.full_duplex = dev->phydev->duplex ? 1 : 0; + link_info.s.speed = dev->phydev->speed; + priv->link_info = link_info.u64; + + /* + * The polling task need to know about link status changes. + */ + if (priv->poll) + priv->poll(dev); + + if (priv->last_link != dev->phydev->link) { + priv->last_link = dev->phydev->link; + cvmx_helper_link_set(priv->port, link_info); + cvm_oct_note_carrier(priv, link_info); + } +} + +int cvm_oct_common_stop(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + int interface = INTERFACE(priv->port); + union cvmx_helper_link_info link_info; + union cvmx_gmxx_prtx_cfg gmx_cfg; + int index = INDEX(priv->port); + + gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + gmx_cfg.s.en = 0; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); + + priv->poll = NULL; + + if (dev->phydev) + phy_disconnect(dev->phydev); + + if (priv->last_link) { + link_info.u64 = 0; + priv->last_link = 0; + + cvmx_helper_link_set(priv->port, link_info); + cvm_oct_note_carrier(priv, link_info); + } + return 0; +} + +/** + * cvm_oct_phy_setup_device - setup the PHY + * + * @dev: Device to setup + * + * Returns Zero on success, negative on failure + */ +int cvm_oct_phy_setup_device(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + struct device_node *phy_node; + struct phy_device *phydev = NULL; + + if (!priv->of_node) + goto no_phy; + + phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0); + if (!phy_node && of_phy_is_fixed_link(priv->of_node)) { + int rc; + + rc = of_phy_register_fixed_link(priv->of_node); + if (rc) + return rc; + + phy_node = of_node_get(priv->of_node); + } + if (!phy_node) + goto no_phy; + + phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0, + priv->phy_mode); + of_node_put(phy_node); + + if (!phydev) + return -ENODEV; + + priv->last_link = 0; + phy_start(phydev); + + return 0; +no_phy: + /* If there is no phy, assume a direct MAC connection and that + * the link is up. + */ + netif_carrier_on(dev); + return 0; +} diff --git a/drivers/staging/octeon/ethernet-mdio.h b/drivers/staging/octeon/ethernet-mdio.h new file mode 100644 index 000000000000..e3771d48c49b --- /dev/null +++ b/drivers/staging/octeon/ethernet-mdio.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_XFRM +#include +#include +#endif /* CONFIG_XFRM */ + +extern const struct ethtool_ops cvm_oct_ethtool_ops; + +void octeon_mdiobus_force_mod_depencency(void); + +int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +int cvm_oct_phy_setup_device(struct net_device *dev); diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c new file mode 100644 index 000000000000..532594957ebc --- /dev/null +++ b/drivers/staging/octeon/ethernet-mem.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2010 Cavium Networks + */ + +#include +#include +#include + +#include "octeon-ethernet.h" +#include "ethernet-mem.h" +#include "ethernet-defines.h" + +/** + * cvm_oct_fill_hw_skbuff - fill the supplied hardware pool with skbuffs + * @pool: Pool to allocate an skbuff for + * @size: Size of the buffer needed for the pool + * @elements: Number of buffers to allocate + * + * Returns the actual number of buffers allocated. + */ +static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements) +{ + int freed = elements; + + while (freed) { + struct sk_buff *skb = dev_alloc_skb(size + 256); + + if (unlikely(!skb)) + break; + skb_reserve(skb, 256 - (((unsigned long)skb->data) & 0x7f)); + *(struct sk_buff **)(skb->data - sizeof(void *)) = skb; + cvmx_fpa_free(skb->data, pool, size / 128); + freed--; + } + return elements - freed; +} + +/** + * cvm_oct_free_hw_skbuff- free hardware pool skbuffs + * @pool: Pool to allocate an skbuff for + * @size: Size of the buffer needed for the pool + * @elements: Number of buffers to allocate + */ +static void cvm_oct_free_hw_skbuff(int pool, int size, int elements) +{ + char *memory; + + do { + memory = cvmx_fpa_alloc(pool); + if (memory) { + struct sk_buff *skb = + *(struct sk_buff **)(memory - sizeof(void *)); + elements--; + dev_kfree_skb(skb); + } + } while (memory); + + if (elements < 0) + pr_warn("Freeing of pool %u had too many skbuffs (%d)\n", + pool, elements); + else if (elements > 0) + pr_warn("Freeing of pool %u is missing %d skbuffs\n", + pool, elements); +} + +/** + * cvm_oct_fill_hw_memory - fill a hardware pool with memory. + * @pool: Pool to populate + * @size: Size of each buffer in the pool + * @elements: Number of buffers to allocate + * + * Returns the actual number of buffers allocated. + */ +static int cvm_oct_fill_hw_memory(int pool, int size, int elements) +{ + char *memory; + char *fpa; + int freed = elements; + + while (freed) { + /* + * FPA memory must be 128 byte aligned. Since we are + * aligning we need to save the original pointer so we + * can feed it to kfree when the memory is returned to + * the kernel. + * + * We allocate an extra 256 bytes to allow for + * alignment and space for the original pointer saved + * just before the block. + */ + memory = kmalloc(size + 256, GFP_ATOMIC); + if (unlikely(!memory)) { + pr_warn("Unable to allocate %u bytes for FPA pool %d\n", + elements * size, pool); + break; + } + fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL); + *((char **)fpa - 1) = memory; + cvmx_fpa_free(fpa, pool, 0); + freed--; + } + return elements - freed; +} + +/** + * cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory + * @pool: FPA pool to free + * @size: Size of each buffer in the pool + * @elements: Number of buffers that should be in the pool + */ +static void cvm_oct_free_hw_memory(int pool, int size, int elements) +{ + char *memory; + char *fpa; + + do { + fpa = cvmx_fpa_alloc(pool); + if (fpa) { + elements--; + fpa = (char *)phys_to_virt(cvmx_ptr_to_phys(fpa)); + memory = *((char **)fpa - 1); + kfree(memory); + } + } while (fpa); + + if (elements < 0) + pr_warn("Freeing of pool %u had too many buffers (%d)\n", + pool, elements); + else if (elements > 0) + pr_warn("Warning: Freeing of pool %u is missing %d buffers\n", + pool, elements); +} + +int cvm_oct_mem_fill_fpa(int pool, int size, int elements) +{ + int freed; + + if (pool == CVMX_FPA_PACKET_POOL) + freed = cvm_oct_fill_hw_skbuff(pool, size, elements); + else + freed = cvm_oct_fill_hw_memory(pool, size, elements); + return freed; +} + +void cvm_oct_mem_empty_fpa(int pool, int size, int elements) +{ + if (pool == CVMX_FPA_PACKET_POOL) + cvm_oct_free_hw_skbuff(pool, size, elements); + else + cvm_oct_free_hw_memory(pool, size, elements); +} diff --git a/drivers/staging/octeon/ethernet-mem.h b/drivers/staging/octeon/ethernet-mem.h new file mode 100644 index 000000000000..692dcdb7154d --- /dev/null +++ b/drivers/staging/octeon/ethernet-mem.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +int cvm_oct_mem_fill_fpa(int pool, int size, int elements); +void cvm_oct_mem_empty_fpa(int pool, int size, int elements); diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c new file mode 100644 index 000000000000..0c4fac31540a --- /dev/null +++ b/drivers/staging/octeon/ethernet-rgmii.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +#include +#include +#include +#include +#include +#include + +#include "octeon-ethernet.h" +#include "ethernet-defines.h" +#include "ethernet-util.h" +#include "ethernet-mdio.h" + +static DEFINE_SPINLOCK(global_register_lock); + +static void cvm_oct_set_hw_preamble(struct octeon_ethernet *priv, bool enable) +{ + union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl; + union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs; + union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg; + int interface = INTERFACE(priv->port); + int index = INDEX(priv->port); + + /* Set preamble checking. */ + gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, + interface)); + gmxx_rxx_frm_ctl.s.pre_chk = enable; + cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), + gmxx_rxx_frm_ctl.u64); + + /* Set FCS stripping. */ + ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS); + if (enable) + ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port; + else + ipd_sub_port_fcs.s.port_bit &= + 0xffffffffull ^ (1ull << priv->port); + cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64); + + /* Clear any error bits. */ + gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, + interface)); + cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), + gmxx_rxx_int_reg.u64); +} + +static void cvm_oct_check_preamble_errors(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + union cvmx_helper_link_info link_info; + unsigned long flags; + + link_info.u64 = priv->link_info; + + /* + * Take the global register lock since we are going to + * touch registers that affect more than one port. + */ + spin_lock_irqsave(&global_register_lock, flags); + + if (link_info.s.speed == 10 && priv->last_speed == 10) { + /* + * Read the GMXX_RXX_INT_REG[PCTERR] bit and see if we are + * getting preamble errors. + */ + int interface = INTERFACE(priv->port); + int index = INDEX(priv->port); + union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg; + + gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG + (index, interface)); + if (gmxx_rxx_int_reg.s.pcterr) { + /* + * We are getting preamble errors at 10Mbps. Most + * likely the PHY is giving us packets with misaligned + * preambles. In order to get these packets we need to + * disable preamble checking and do it in software. + */ + cvm_oct_set_hw_preamble(priv, false); + printk_ratelimited("%s: Using 10Mbps with software preamble removal\n", + dev->name); + } + } else { + /* + * Since the 10Mbps preamble workaround is allowed we need to + * enable preamble checking, FCS stripping, and clear error + * bits on every speed change. If errors occur during 10Mbps + * operation the above code will change this stuff + */ + if (priv->last_speed != link_info.s.speed) + cvm_oct_set_hw_preamble(priv, true); + priv->last_speed = link_info.s.speed; + } + spin_unlock_irqrestore(&global_register_lock, flags); +} + +static void cvm_oct_rgmii_poll(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + union cvmx_helper_link_info link_info; + bool status_change; + + link_info = cvmx_helper_link_get(priv->port); + if (priv->link_info != link_info.u64 && + cvmx_helper_link_set(priv->port, link_info)) + link_info.u64 = priv->link_info; + status_change = priv->link_info != link_info.u64; + priv->link_info = link_info.u64; + + cvm_oct_check_preamble_errors(dev); + + if (likely(!status_change)) + return; + + /* Tell core. */ + if (link_info.s.link_up) { + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + } else if (netif_carrier_ok(dev)) { + netif_carrier_off(dev); + } + cvm_oct_note_carrier(priv, link_info); +} + +int cvm_oct_rgmii_open(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + int ret; + + ret = cvm_oct_common_open(dev, cvm_oct_rgmii_poll); + if (ret) + return ret; + + if (dev->phydev) { + /* + * In phydev mode, we need still periodic polling for the + * preamble error checking, and we also need to call this + * function on every link state change. + * + * Only true RGMII ports need to be polled. In GMII mode, port + * 0 is really a RGMII port. + */ + if ((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII && + priv->port == 0) || + (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) { + priv->poll = cvm_oct_check_preamble_errors; + cvm_oct_check_preamble_errors(dev); + } + } + + return 0; +} diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c new file mode 100644 index 000000000000..2c16230f993c --- /dev/null +++ b/drivers/staging/octeon/ethernet-rx.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2010 Cavium Networks + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_XFRM +#include +#include +#endif /* CONFIG_XFRM */ + +#include "octeon-ethernet.h" +#include "ethernet-defines.h" +#include "ethernet-mem.h" +#include "ethernet-rx.h" +#include "ethernet-util.h" + +static atomic_t oct_rx_ready = ATOMIC_INIT(0); + +static struct oct_rx_group { + int irq; + int group; + struct napi_struct napi; +} oct_rx_group[16]; + +/** + * cvm_oct_do_interrupt - interrupt handler. + * @irq: Interrupt number. + * @napi_id: Cookie to identify the NAPI instance. + * + * The interrupt occurs whenever the POW has packets in our group. + * + */ +static irqreturn_t cvm_oct_do_interrupt(int irq, void *napi_id) +{ + /* Disable the IRQ and start napi_poll. */ + disable_irq_nosync(irq); + napi_schedule(napi_id); + + return IRQ_HANDLED; +} + +/** + * cvm_oct_check_rcv_error - process receive errors + * @work: Work queue entry pointing to the packet. + * + * Returns Non-zero if the packet can be dropped, zero otherwise. + */ +static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) +{ + int port; + + if (octeon_has_feature(OCTEON_FEATURE_PKND)) + port = work->word0.pip.cn68xx.pknd; + else + port = work->word1.cn38xx.ipprt; + + if ((work->word2.snoip.err_code == 10) && (work->word1.len <= 64)) { + /* + * Ignore length errors on min size packets. Some + * equipment incorrectly pads packets to 64+4FCS + * instead of 60+4FCS. Note these packets still get + * counted as frame errors. + */ + } else if (work->word2.snoip.err_code == 5 || + work->word2.snoip.err_code == 7) { + /* + * We received a packet with either an alignment error + * or a FCS error. This may be signalling that we are + * running 10Mbps with GMXX_RXX_FRM_CTL[PRE_CHK] + * off. If this is the case we need to parse the + * packet to determine if we can remove a non spec + * preamble and generate a correct packet. + */ + int interface = cvmx_helper_get_interface_num(port); + int index = cvmx_helper_get_interface_index_num(port); + union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl; + + gmxx_rxx_frm_ctl.u64 = + cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface)); + if (gmxx_rxx_frm_ctl.s.pre_chk == 0) { + u8 *ptr = + cvmx_phys_to_ptr(work->packet_ptr.s.addr); + int i = 0; + + while (i < work->word1.len - 1) { + if (*ptr != 0x55) + break; + ptr++; + i++; + } + + if (*ptr == 0xd5) { + /* Port received 0xd5 preamble */ + work->packet_ptr.s.addr += i + 1; + work->word1.len -= i + 5; + } else if ((*ptr & 0xf) == 0xd) { + /* Port received 0xd preamble */ + work->packet_ptr.s.addr += i; + work->word1.len -= i + 4; + for (i = 0; i < work->word1.len; i++) { + *ptr = + ((*ptr & 0xf0) >> 4) | + ((*(ptr + 1) & 0xf) << 4); + ptr++; + } + } else { + printk_ratelimited("Port %d unknown preamble, packet dropped\n", + port); + cvm_oct_free_work(work); + return 1; + } + } + } else { + printk_ratelimited("Port %d receive error code %d, packet dropped\n", + port, work->word2.snoip.err_code); + cvm_oct_free_work(work); + return 1; + } + + return 0; +} + +static void copy_segments_to_skb(struct cvmx_wqe *work, struct sk_buff *skb) +{ + int segments = work->word2.s.bufs; + union cvmx_buf_ptr segment_ptr = work->packet_ptr; + int len = work->word1.len; + int segment_size; + + while (segments--) { + union cvmx_buf_ptr next_ptr; + + next_ptr = *(union cvmx_buf_ptr *) + cvmx_phys_to_ptr(segment_ptr.s.addr - 8); + + /* + * Octeon Errata PKI-100: The segment size is wrong. + * + * Until it is fixed, calculate the segment size based on + * the packet pool buffer size. + * When it is fixed, the following line should be replaced + * with this one: + * int segment_size = segment_ptr.s.size; + */ + segment_size = + CVMX_FPA_PACKET_POOL_SIZE - + (segment_ptr.s.addr - + (((segment_ptr.s.addr >> 7) - + segment_ptr.s.back) << 7)); + + /* Don't copy more than what is left in the packet */ + if (segment_size > len) + segment_size = len; + + /* Copy the data into the packet */ + skb_put_data(skb, cvmx_phys_to_ptr(segment_ptr.s.addr), + segment_size); + len -= segment_size; + segment_ptr = next_ptr; + } +} + +static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget) +{ + const int coreid = cvmx_get_core_num(); + u64 old_group_mask; + u64 old_scratch; + int rx_count = 0; + int did_work_request = 0; + int packet_not_copied; + + /* Prefetch cvm_oct_device since we know we need it soon */ + prefetch(cvm_oct_device); + + if (USE_ASYNC_IOBDMA) { + /* Save scratch in case userspace is using it */ + CVMX_SYNCIOBDMA; + old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH); + } + + /* Only allow work for our group (and preserve priorities) */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + old_group_mask = cvmx_read_csr(CVMX_SSO_PPX_GRP_MSK(coreid)); + cvmx_write_csr(CVMX_SSO_PPX_GRP_MSK(coreid), + BIT(rx_group->group)); + cvmx_read_csr(CVMX_SSO_PPX_GRP_MSK(coreid)); /* Flush */ + } else { + old_group_mask = cvmx_read_csr(CVMX_POW_PP_GRP_MSKX(coreid)); + cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), + (old_group_mask & ~0xFFFFull) | + BIT(rx_group->group)); + } + + if (USE_ASYNC_IOBDMA) { + cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT); + did_work_request = 1; + } + + while (rx_count < budget) { + struct sk_buff *skb = NULL; + struct sk_buff **pskb = NULL; + int skb_in_hw; + struct cvmx_wqe *work; + int port; + + if (USE_ASYNC_IOBDMA && did_work_request) + work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH); + else + work = cvmx_pow_work_request_sync(CVMX_POW_NO_WAIT); + + prefetch(work); + did_work_request = 0; + if (!work) { + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + cvmx_write_csr(CVMX_SSO_WQ_IQ_DIS, + BIT(rx_group->group)); + cvmx_write_csr(CVMX_SSO_WQ_INT, + BIT(rx_group->group)); + } else { + union cvmx_pow_wq_int wq_int; + + wq_int.u64 = 0; + wq_int.s.iq_dis = BIT(rx_group->group); + wq_int.s.wq_int = BIT(rx_group->group); + cvmx_write_csr(CVMX_POW_WQ_INT, wq_int.u64); + } + break; + } + pskb = (struct sk_buff **) + (cvm_oct_get_buffer_ptr(work->packet_ptr) - + sizeof(void *)); + prefetch(pskb); + + if (USE_ASYNC_IOBDMA && rx_count < (budget - 1)) { + cvmx_pow_work_request_async_nocheck(CVMX_SCR_SCRATCH, + CVMX_POW_NO_WAIT); + did_work_request = 1; + } + rx_count++; + + skb_in_hw = work->word2.s.bufs == 1; + if (likely(skb_in_hw)) { + skb = *pskb; + prefetch(&skb->head); + prefetch(&skb->len); + } + + if (octeon_has_feature(OCTEON_FEATURE_PKND)) + port = work->word0.pip.cn68xx.pknd; + else + port = work->word1.cn38xx.ipprt; + + prefetch(cvm_oct_device[port]); + + /* Immediately throw away all packets with receive errors */ + if (unlikely(work->word2.snoip.rcv_error)) { + if (cvm_oct_check_rcv_error(work)) + continue; + } + + /* + * We can only use the zero copy path if skbuffs are + * in the FPA pool and the packet fits in a single + * buffer. + */ + if (likely(skb_in_hw)) { + skb->data = skb->head + work->packet_ptr.s.addr - + cvmx_ptr_to_phys(skb->head); + prefetch(skb->data); + skb->len = work->word1.len; + skb_set_tail_pointer(skb, skb->len); + packet_not_copied = 1; + } else { + /* + * We have to copy the packet. First allocate + * an skbuff for it. + */ + skb = dev_alloc_skb(work->word1.len); + if (!skb) { + cvm_oct_free_work(work); + continue; + } + + /* + * Check if we've received a packet that was + * entirely stored in the work entry. + */ + if (unlikely(work->word2.s.bufs == 0)) { + u8 *ptr = work->packet_data; + + if (likely(!work->word2.s.not_IP)) { + /* + * The beginning of the packet + * moves for IP packets. + */ + if (work->word2.s.is_v6) + ptr += 2; + else + ptr += 6; + } + skb_put_data(skb, ptr, work->word1.len); + /* No packet buffers to free */ + } else { + copy_segments_to_skb(work, skb); + } + packet_not_copied = 0; + } + if (likely((port < TOTAL_NUMBER_OF_PORTS) && + cvm_oct_device[port])) { + struct net_device *dev = cvm_oct_device[port]; + + /* + * Only accept packets for devices that are + * currently up. + */ + if (likely(dev->flags & IFF_UP)) { + skb->protocol = eth_type_trans(skb, dev); + skb->dev = dev; + + if (unlikely(work->word2.s.not_IP || + work->word2.s.IP_exc || + work->word2.s.L4_error || + !work->word2.s.tcp_or_udp)) + skb->ip_summed = CHECKSUM_NONE; + else + skb->ip_summed = CHECKSUM_UNNECESSARY; + + /* Increment RX stats for virtual ports */ + if (port >= CVMX_PIP_NUM_INPUT_PORTS) { + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + } + netif_receive_skb(skb); + } else { + /* + * Drop any packet received for a device that + * isn't up. + */ + dev->stats.rx_dropped++; + dev_kfree_skb_irq(skb); + } + } else { + /* + * Drop any packet received for a device that + * doesn't exist. + */ + printk_ratelimited("Port %d not controlled by Linux, packet dropped\n", + port); + dev_kfree_skb_irq(skb); + } + /* + * Check to see if the skbuff and work share the same + * packet buffer. + */ + if (likely(packet_not_copied)) { + /* + * This buffer needs to be replaced, increment + * the number of buffers we need to free by + * one. + */ + cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, + 1); + + cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, 1); + } else { + cvm_oct_free_work(work); + } + } + /* Restore the original POW group mask */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + cvmx_write_csr(CVMX_SSO_PPX_GRP_MSK(coreid), old_group_mask); + cvmx_read_csr(CVMX_SSO_PPX_GRP_MSK(coreid)); /* Flush */ + } else { + cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask); + } + + if (USE_ASYNC_IOBDMA) { + /* Restore the scratch area */ + cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch); + } + cvm_oct_rx_refill_pool(0); + + return rx_count; +} + +/** + * cvm_oct_napi_poll - the NAPI poll function. + * @napi: The NAPI instance. + * @budget: Maximum number of packets to receive. + * + * Returns the number of packets processed. + */ +static int cvm_oct_napi_poll(struct napi_struct *napi, int budget) +{ + struct oct_rx_group *rx_group = container_of(napi, struct oct_rx_group, + napi); + int rx_count; + + rx_count = cvm_oct_poll(rx_group, budget); + + if (rx_count < budget) { + /* No more work */ + napi_complete_done(napi, rx_count); + enable_irq(rx_group->irq); + } + return rx_count; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/** + * cvm_oct_poll_controller - poll for receive packets + * device. + * + * @dev: Device to poll. Unused + */ +void cvm_oct_poll_controller(struct net_device *dev) +{ + int i; + + if (!atomic_read(&oct_rx_ready)) + return; + + for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { + if (!(pow_receive_groups & BIT(i))) + continue; + + cvm_oct_poll(&oct_rx_group[i], 16); + } +} +#endif + +void cvm_oct_rx_initialize(void) +{ + int i; + struct net_device *dev_for_napi = NULL; + + for (i = 0; i < TOTAL_NUMBER_OF_PORTS; i++) { + if (cvm_oct_device[i]) { + dev_for_napi = cvm_oct_device[i]; + break; + } + } + + if (!dev_for_napi) + panic("No net_devices were allocated."); + + for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { + int ret; + + if (!(pow_receive_groups & BIT(i))) + continue; + + netif_napi_add(dev_for_napi, &oct_rx_group[i].napi, + cvm_oct_napi_poll, rx_napi_weight); + napi_enable(&oct_rx_group[i].napi); + + oct_rx_group[i].irq = OCTEON_IRQ_WORKQ0 + i; + oct_rx_group[i].group = i; + + /* Register an IRQ handler to receive POW interrupts */ + ret = request_irq(oct_rx_group[i].irq, cvm_oct_do_interrupt, 0, + "Ethernet", &oct_rx_group[i].napi); + if (ret) + panic("Could not acquire Ethernet IRQ %d\n", + oct_rx_group[i].irq); + + disable_irq_nosync(oct_rx_group[i].irq); + + /* Enable POW interrupt when our port has at least one packet */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { + union cvmx_sso_wq_int_thrx int_thr; + union cvmx_pow_wq_int_pc int_pc; + + int_thr.u64 = 0; + int_thr.s.tc_en = 1; + int_thr.s.tc_thr = 1; + cvmx_write_csr(CVMX_SSO_WQ_INT_THRX(i), int_thr.u64); + + int_pc.u64 = 0; + int_pc.s.pc_thr = 5; + cvmx_write_csr(CVMX_SSO_WQ_INT_PC, int_pc.u64); + } else { + union cvmx_pow_wq_int_thrx int_thr; + union cvmx_pow_wq_int_pc int_pc; + + int_thr.u64 = 0; + int_thr.s.tc_en = 1; + int_thr.s.tc_thr = 1; + cvmx_write_csr(CVMX_POW_WQ_INT_THRX(i), int_thr.u64); + + int_pc.u64 = 0; + int_pc.s.pc_thr = 5; + cvmx_write_csr(CVMX_POW_WQ_INT_PC, int_pc.u64); + } + + /* Schedule NAPI now. This will indirectly enable the + * interrupt. + */ + napi_schedule(&oct_rx_group[i].napi); + } + atomic_inc(&oct_rx_ready); +} + +void cvm_oct_rx_shutdown(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { + if (!(pow_receive_groups & BIT(i))) + continue; + + /* Disable POW interrupt */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + cvmx_write_csr(CVMX_SSO_WQ_INT_THRX(i), 0); + else + cvmx_write_csr(CVMX_POW_WQ_INT_THRX(i), 0); + + /* Free the interrupt handler */ + free_irq(oct_rx_group[i].irq, cvm_oct_device); + + netif_napi_del(&oct_rx_group[i].napi); + } +} diff --git a/drivers/staging/octeon/ethernet-rx.h b/drivers/staging/octeon/ethernet-rx.h new file mode 100644 index 000000000000..ff6482fa20d6 --- /dev/null +++ b/drivers/staging/octeon/ethernet-rx.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +void cvm_oct_poll_controller(struct net_device *dev); +void cvm_oct_rx_initialize(void); +void cvm_oct_rx_shutdown(void); + +static inline void cvm_oct_rx_refill_pool(int fill_threshold) +{ + int number_to_free; + int num_freed; + /* Refill the packet buffer pool */ + number_to_free = + cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); + + if (number_to_free > fill_threshold) { + cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, + -number_to_free); + num_freed = cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, + CVMX_FPA_PACKET_POOL_SIZE, + number_to_free); + if (num_freed != number_to_free) { + cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, + number_to_free - num_freed); + } + } +} diff --git a/drivers/staging/octeon/ethernet-sgmii.c b/drivers/staging/octeon/ethernet-sgmii.c new file mode 100644 index 000000000000..d7fbd9159302 --- /dev/null +++ b/drivers/staging/octeon/ethernet-sgmii.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +#include +#include +#include +#include +#include + +#include "octeon-ethernet.h" +#include "ethernet-defines.h" +#include "ethernet-util.h" +#include "ethernet-mdio.h" + +int cvm_oct_sgmii_open(struct net_device *dev) +{ + return cvm_oct_common_open(dev, cvm_oct_link_poll); +} + +int cvm_oct_sgmii_init(struct net_device *dev) +{ + cvm_oct_common_init(dev); + + /* FIXME: Need autoneg logic */ + return 0; +} diff --git a/drivers/staging/octeon/ethernet-spi.c b/drivers/staging/octeon/ethernet-spi.c new file mode 100644 index 000000000000..c582403e6a1f --- /dev/null +++ b/drivers/staging/octeon/ethernet-spi.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +#include +#include +#include +#include + +#include "octeon-ethernet.h" +#include "ethernet-defines.h" +#include "ethernet-util.h" + +static int number_spi_ports; +static int need_retrain[2] = { 0, 0 }; + +static void cvm_oct_spxx_int_pr(union cvmx_spxx_int_reg spx_int_reg, int index) +{ + if (spx_int_reg.s.spf) + pr_err("SPI%d: SRX Spi4 interface down\n", index); + if (spx_int_reg.s.calerr) + pr_err("SPI%d: SRX Spi4 Calendar table parity error\n", index); + if (spx_int_reg.s.syncerr) + pr_err("SPI%d: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n", + index); + if (spx_int_reg.s.diperr) + pr_err("SPI%d: SRX Spi4 DIP4 error\n", index); + if (spx_int_reg.s.tpaovr) + pr_err("SPI%d: SRX Selected port has hit TPA overflow\n", + index); + if (spx_int_reg.s.rsverr) + pr_err("SPI%d: SRX Spi4 reserved control word detected\n", + index); + if (spx_int_reg.s.drwnng) + pr_err("SPI%d: SRX Spi4 receive FIFO drowning/overflow\n", + index); + if (spx_int_reg.s.clserr) + pr_err("SPI%d: SRX Spi4 packet closed on non-16B alignment without EOP\n", + index); + if (spx_int_reg.s.spiovr) + pr_err("SPI%d: SRX Spi4 async FIFO overflow\n", index); + if (spx_int_reg.s.abnorm) + pr_err("SPI%d: SRX Abnormal packet termination (ERR bit)\n", + index); + if (spx_int_reg.s.prtnxa) + pr_err("SPI%d: SRX Port out of range\n", index); +} + +static void cvm_oct_stxx_int_pr(union cvmx_stxx_int_reg stx_int_reg, int index) +{ + if (stx_int_reg.s.syncerr) + pr_err("SPI%d: STX Interface encountered a fatal error\n", + index); + if (stx_int_reg.s.frmerr) + pr_err("SPI%d: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n", + index); + if (stx_int_reg.s.unxfrm) + pr_err("SPI%d: STX Unexpected framing sequence\n", index); + if (stx_int_reg.s.nosync) + pr_err("SPI%d: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n", + index); + if (stx_int_reg.s.diperr) + pr_err("SPI%d: STX DIP2 error on the Spi4 Status channel\n", + index); + if (stx_int_reg.s.datovr) + pr_err("SPI%d: STX Spi4 FIFO overflow error\n", index); + if (stx_int_reg.s.ovrbst) + pr_err("SPI%d: STX Transmit packet burst too big\n", index); + if (stx_int_reg.s.calpar1) + pr_err("SPI%d: STX Calendar Table Parity Error Bank%d\n", + index, 1); + if (stx_int_reg.s.calpar0) + pr_err("SPI%d: STX Calendar Table Parity Error Bank%d\n", + index, 0); +} + +static irqreturn_t cvm_oct_spi_spx_int(int index) +{ + union cvmx_spxx_int_reg spx_int_reg; + union cvmx_stxx_int_reg stx_int_reg; + + spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(index)); + cvmx_write_csr(CVMX_SPXX_INT_REG(index), spx_int_reg.u64); + if (!need_retrain[index]) { + spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(index)); + cvm_oct_spxx_int_pr(spx_int_reg, index); + } + + stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(index)); + cvmx_write_csr(CVMX_STXX_INT_REG(index), stx_int_reg.u64); + if (!need_retrain[index]) { + stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(index)); + cvm_oct_stxx_int_pr(stx_int_reg, index); + } + + cvmx_write_csr(CVMX_SPXX_INT_MSK(index), 0); + cvmx_write_csr(CVMX_STXX_INT_MSK(index), 0); + need_retrain[index] = 1; + + return IRQ_HANDLED; +} + +static irqreturn_t cvm_oct_spi_rml_interrupt(int cpl, void *dev_id) +{ + irqreturn_t return_status = IRQ_NONE; + union cvmx_npi_rsl_int_blocks rsl_int_blocks; + + /* Check and see if this interrupt was caused by the GMX block */ + rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS); + if (rsl_int_blocks.s.spx1) /* 19 - SPX1_INT_REG & STX1_INT_REG */ + return_status = cvm_oct_spi_spx_int(1); + + if (rsl_int_blocks.s.spx0) /* 18 - SPX0_INT_REG & STX0_INT_REG */ + return_status = cvm_oct_spi_spx_int(0); + + return return_status; +} + +static void cvm_oct_spi_enable_error_reporting(int interface) +{ + union cvmx_spxx_int_msk spxx_int_msk; + union cvmx_stxx_int_msk stxx_int_msk; + + spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface)); + spxx_int_msk.s.calerr = 1; + spxx_int_msk.s.syncerr = 1; + spxx_int_msk.s.diperr = 1; + spxx_int_msk.s.tpaovr = 1; + spxx_int_msk.s.rsverr = 1; + spxx_int_msk.s.drwnng = 1; + spxx_int_msk.s.clserr = 1; + spxx_int_msk.s.spiovr = 1; + spxx_int_msk.s.abnorm = 1; + spxx_int_msk.s.prtnxa = 1; + cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64); + + stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface)); + stxx_int_msk.s.frmerr = 1; + stxx_int_msk.s.unxfrm = 1; + stxx_int_msk.s.nosync = 1; + stxx_int_msk.s.diperr = 1; + stxx_int_msk.s.datovr = 1; + stxx_int_msk.s.ovrbst = 1; + stxx_int_msk.s.calpar1 = 1; + stxx_int_msk.s.calpar0 = 1; + cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64); +} + +static void cvm_oct_spi_poll(struct net_device *dev) +{ + static int spi4000_port; + struct octeon_ethernet *priv = netdev_priv(dev); + int interface; + + for (interface = 0; interface < 2; interface++) { + if ((priv->port == interface * 16) && need_retrain[interface]) { + if (cvmx_spi_restart_interface + (interface, CVMX_SPI_MODE_DUPLEX, 10) == 0) { + need_retrain[interface] = 0; + cvm_oct_spi_enable_error_reporting(interface); + } + } + + /* + * The SPI4000 TWSI interface is very slow. In order + * not to bring the system to a crawl, we only poll a + * single port every second. This means negotiation + * speed changes take up to 10 seconds, but at least + * we don't waste absurd amounts of time waiting for + * TWSI. + */ + if (priv->port == spi4000_port) { + /* + * This function does nothing if it is called on an + * interface without a SPI4000. + */ + cvmx_spi4000_check_speed(interface, priv->port); + /* + * Normal ordering increments. By decrementing + * we only match once per iteration. + */ + spi4000_port--; + if (spi4000_port < 0) + spi4000_port = 10; + } + } +} + +int cvm_oct_spi_init(struct net_device *dev) +{ + int r; + struct octeon_ethernet *priv = netdev_priv(dev); + + if (number_spi_ports == 0) { + r = request_irq(OCTEON_IRQ_RML, cvm_oct_spi_rml_interrupt, + IRQF_SHARED, "SPI", &number_spi_ports); + if (r) + return r; + } + number_spi_ports++; + + if ((priv->port == 0) || (priv->port == 16)) { + cvm_oct_spi_enable_error_reporting(INTERFACE(priv->port)); + priv->poll = cvm_oct_spi_poll; + } + cvm_oct_common_init(dev); + return 0; +} + +void cvm_oct_spi_uninit(struct net_device *dev) +{ + int interface; + + cvm_oct_common_uninit(dev); + number_spi_ports--; + if (number_spi_ports == 0) { + for (interface = 0; interface < 2; interface++) { + cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0); + cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0); + } + free_irq(OCTEON_IRQ_RML, &number_spi_ports); + } +} diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c new file mode 100644 index 000000000000..b334cf89794e --- /dev/null +++ b/drivers/staging/octeon/ethernet-tx.c @@ -0,0 +1,717 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2010 Cavium Networks + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_XFRM +#include +#include +#endif /* CONFIG_XFRM */ + +#include +#include + +#include "octeon-ethernet.h" +#include "ethernet-defines.h" +#include "ethernet-tx.h" +#include "ethernet-util.h" + +#define CVM_OCT_SKB_CB(skb) ((u64 *)((skb)->cb)) + +/* + * You can define GET_SKBUFF_QOS() to override how the skbuff output + * function determines which output queue is used. The default + * implementation always uses the base queue for the port. If, for + * example, you wanted to use the skb->priority field, define + * GET_SKBUFF_QOS as: #define GET_SKBUFF_QOS(skb) ((skb)->priority) + */ +#ifndef GET_SKBUFF_QOS +#define GET_SKBUFF_QOS(skb) 0 +#endif + +static void cvm_oct_tx_do_cleanup(unsigned long arg); +static DECLARE_TASKLET(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup, 0); + +/* Maximum number of SKBs to try to free per xmit packet. */ +#define MAX_SKB_TO_FREE (MAX_OUT_QUEUE_DEPTH * 2) + +static inline int cvm_oct_adjust_skb_to_free(int skb_to_free, int fau) +{ + int undo; + + undo = skb_to_free > 0 ? MAX_SKB_TO_FREE : skb_to_free + + MAX_SKB_TO_FREE; + if (undo > 0) + cvmx_fau_atomic_add32(fau, -undo); + skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ? MAX_SKB_TO_FREE : + -skb_to_free; + return skb_to_free; +} + +static void cvm_oct_kick_tx_poll_watchdog(void) +{ + union cvmx_ciu_timx ciu_timx; + + ciu_timx.u64 = 0; + ciu_timx.s.one_shot = 1; + ciu_timx.s.len = cvm_oct_tx_poll_interval; + cvmx_write_csr(CVMX_CIU_TIMX(1), ciu_timx.u64); +} + +static void cvm_oct_free_tx_skbs(struct net_device *dev) +{ + int skb_to_free; + int qos, queues_per_port; + int total_freed = 0; + int total_remaining = 0; + unsigned long flags; + struct octeon_ethernet *priv = netdev_priv(dev); + + queues_per_port = cvmx_pko_get_num_queues(priv->port); + /* Drain any pending packets in the free list */ + for (qos = 0; qos < queues_per_port; qos++) { + if (skb_queue_len(&priv->tx_free_list[qos]) == 0) + continue; + skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4, + MAX_SKB_TO_FREE); + skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, + priv->fau + qos * 4); + total_freed += skb_to_free; + if (skb_to_free > 0) { + struct sk_buff *to_free_list = NULL; + + spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); + while (skb_to_free > 0) { + struct sk_buff *t; + + t = __skb_dequeue(&priv->tx_free_list[qos]); + t->next = to_free_list; + to_free_list = t; + skb_to_free--; + } + spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, + flags); + /* Do the actual freeing outside of the lock. */ + while (to_free_list) { + struct sk_buff *t = to_free_list; + + to_free_list = to_free_list->next; + dev_kfree_skb_any(t); + } + } + total_remaining += skb_queue_len(&priv->tx_free_list[qos]); + } + if (total_remaining < MAX_OUT_QUEUE_DEPTH && netif_queue_stopped(dev)) + netif_wake_queue(dev); + if (total_remaining) + cvm_oct_kick_tx_poll_watchdog(); +} + +/** + * cvm_oct_xmit - transmit a packet + * @skb: Packet to send + * @dev: Device info structure + * + * Returns Always returns NETDEV_TX_OK + */ +int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev) +{ + union cvmx_pko_command_word0 pko_command; + union cvmx_buf_ptr hw_buffer; + u64 old_scratch; + u64 old_scratch2; + int qos; + int i; + enum {QUEUE_CORE, QUEUE_HW, QUEUE_DROP} queue_type; + struct octeon_ethernet *priv = netdev_priv(dev); + struct sk_buff *to_free_list; + int skb_to_free; + int buffers_to_free; + u32 total_to_clean; + unsigned long flags; +#if REUSE_SKBUFFS_WITHOUT_FREE + unsigned char *fpa_head; +#endif + + /* + * Prefetch the private data structure. It is larger than the + * one cache line. + */ + prefetch(priv); + + /* + * The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to + * completely remove "qos" in the event neither interface + * supports multiple queues per port. + */ + if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) || + (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) { + qos = GET_SKBUFF_QOS(skb); + if (qos <= 0) + qos = 0; + else if (qos >= cvmx_pko_get_num_queues(priv->port)) + qos = 0; + } else { + qos = 0; + } + + if (USE_ASYNC_IOBDMA) { + /* Save scratch in case userspace is using it */ + CVMX_SYNCIOBDMA; + old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH); + old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8); + + /* + * Fetch and increment the number of packets to be + * freed. + */ + cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH + 8, + FAU_NUM_PACKET_BUFFERS_TO_FREE, + 0); + cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, + priv->fau + qos * 4, + MAX_SKB_TO_FREE); + } + + /* + * We have space for 6 segment pointers, If there will be more + * than that, we must linearize. + */ + if (unlikely(skb_shinfo(skb)->nr_frags > 5)) { + if (unlikely(__skb_linearize(skb))) { + queue_type = QUEUE_DROP; + if (USE_ASYNC_IOBDMA) { + /* + * Get the number of skbuffs in use + * by the hardware + */ + CVMX_SYNCIOBDMA; + skb_to_free = + cvmx_scratch_read64(CVMX_SCR_SCRATCH); + } else { + /* + * Get the number of skbuffs in use + * by the hardware + */ + skb_to_free = + cvmx_fau_fetch_and_add32(priv->fau + + qos * 4, + MAX_SKB_TO_FREE); + } + skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, + priv->fau + + qos * 4); + spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); + goto skip_xmit; + } + } + + /* + * The CN3XXX series of parts has an errata (GMX-401) which + * causes the GMX block to hang if a collision occurs towards + * the end of a <68 byte packet. As a workaround for this, we + * pad packets to be 68 bytes whenever we are in half duplex + * mode. We don't handle the case of having a small packet but + * no room to add the padding. The kernel should always give + * us at least a cache line + */ + if ((skb->len < 64) && OCTEON_IS_MODEL(OCTEON_CN3XXX)) { + union cvmx_gmxx_prtx_cfg gmx_prt_cfg; + int interface = INTERFACE(priv->port); + int index = INDEX(priv->port); + + if (interface < 2) { + /* We only need to pad packet in half duplex mode */ + gmx_prt_cfg.u64 = + cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + if (gmx_prt_cfg.s.duplex == 0) { + int add_bytes = 64 - skb->len; + + if ((skb_tail_pointer(skb) + add_bytes) <= + skb_end_pointer(skb)) + __skb_put_zero(skb, add_bytes); + } + } + } + + /* Build the PKO command */ + pko_command.u64 = 0; +#ifdef __LITTLE_ENDIAN + pko_command.s.le = 1; +#endif + pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */ + pko_command.s.segs = 1; + pko_command.s.total_bytes = skb->len; + pko_command.s.size0 = CVMX_FAU_OP_SIZE_32; + pko_command.s.subone0 = 1; + + pko_command.s.dontfree = 1; + + /* Build the PKO buffer pointer */ + hw_buffer.u64 = 0; + if (skb_shinfo(skb)->nr_frags == 0) { + hw_buffer.s.addr = XKPHYS_TO_PHYS((uintptr_t)skb->data); + hw_buffer.s.pool = 0; + hw_buffer.s.size = skb->len; + } else { + hw_buffer.s.addr = XKPHYS_TO_PHYS((uintptr_t)skb->data); + hw_buffer.s.pool = 0; + hw_buffer.s.size = skb_headlen(skb); + CVM_OCT_SKB_CB(skb)[0] = hw_buffer.u64; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *fs = skb_shinfo(skb)->frags + i; + + hw_buffer.s.addr = + XKPHYS_TO_PHYS((uintptr_t)skb_frag_address(fs)); + hw_buffer.s.size = skb_frag_size(fs); + CVM_OCT_SKB_CB(skb)[i + 1] = hw_buffer.u64; + } + hw_buffer.s.addr = + XKPHYS_TO_PHYS((uintptr_t)CVM_OCT_SKB_CB(skb)); + hw_buffer.s.size = skb_shinfo(skb)->nr_frags + 1; + pko_command.s.segs = skb_shinfo(skb)->nr_frags + 1; + pko_command.s.gather = 1; + goto dont_put_skbuff_in_hw; + } + + /* + * See if we can put this skb in the FPA pool. Any strange + * behavior from the Linux networking stack will most likely + * be caused by a bug in the following code. If some field is + * in use by the network stack and gets carried over when a + * buffer is reused, bad things may happen. If in doubt and + * you dont need the absolute best performance, disable the + * define REUSE_SKBUFFS_WITHOUT_FREE. The reuse of buffers has + * shown a 25% increase in performance under some loads. + */ +#if REUSE_SKBUFFS_WITHOUT_FREE + fpa_head = skb->head + 256 - ((unsigned long)skb->head & 0x7f); + if (unlikely(skb->data < fpa_head)) { + /* TX buffer beginning can't meet FPA alignment constraints */ + goto dont_put_skbuff_in_hw; + } + if (unlikely + ((skb_end_pointer(skb) - fpa_head) < CVMX_FPA_PACKET_POOL_SIZE)) { + /* TX buffer isn't large enough for the FPA */ + goto dont_put_skbuff_in_hw; + } + if (unlikely(skb_shared(skb))) { + /* TX buffer sharing data with someone else */ + goto dont_put_skbuff_in_hw; + } + if (unlikely(skb_cloned(skb))) { + /* TX buffer has been cloned */ + goto dont_put_skbuff_in_hw; + } + if (unlikely(skb_header_cloned(skb))) { + /* TX buffer header has been cloned */ + goto dont_put_skbuff_in_hw; + } + if (unlikely(skb->destructor)) { + /* TX buffer has a destructor */ + goto dont_put_skbuff_in_hw; + } + if (unlikely(skb_shinfo(skb)->nr_frags)) { + /* TX buffer has fragments */ + goto dont_put_skbuff_in_hw; + } + if (unlikely + (skb->truesize != + sizeof(*skb) + skb_end_offset(skb))) { + /* TX buffer truesize has been changed */ + goto dont_put_skbuff_in_hw; + } + + /* + * We can use this buffer in the FPA. We don't need the FAU + * update anymore + */ + pko_command.s.dontfree = 0; + + hw_buffer.s.back = ((unsigned long)skb->data >> 7) - + ((unsigned long)fpa_head >> 7); + + *(struct sk_buff **)(fpa_head - sizeof(void *)) = skb; + + /* + * The skbuff will be reused without ever being freed. We must + * cleanup a bunch of core things. + */ + dst_release(skb_dst(skb)); + skb_dst_set(skb, NULL); + skb_ext_reset(skb); + nf_reset_ct(skb); + +#ifdef CONFIG_NET_SCHED + skb->tc_index = 0; + skb_reset_tc(skb); +#endif /* CONFIG_NET_SCHED */ +#endif /* REUSE_SKBUFFS_WITHOUT_FREE */ + +dont_put_skbuff_in_hw: + + /* Check if we can use the hardware checksumming */ + if ((skb->protocol == htons(ETH_P_IP)) && + (ip_hdr(skb)->version == 4) && + (ip_hdr(skb)->ihl == 5) && + ((ip_hdr(skb)->frag_off == 0) || + (ip_hdr(skb)->frag_off == htons(1 << 14))) && + ((ip_hdr(skb)->protocol == IPPROTO_TCP) || + (ip_hdr(skb)->protocol == IPPROTO_UDP))) { + /* Use hardware checksum calc */ + pko_command.s.ipoffp1 = skb_network_offset(skb) + 1; + } + + if (USE_ASYNC_IOBDMA) { + /* Get the number of skbuffs in use by the hardware */ + CVMX_SYNCIOBDMA; + skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH); + buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8); + } else { + /* Get the number of skbuffs in use by the hardware */ + skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4, + MAX_SKB_TO_FREE); + buffers_to_free = + cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); + } + + skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, + priv->fau + qos * 4); + + /* + * If we're sending faster than the receive can free them then + * don't do the HW free. + */ + if ((buffers_to_free < -100) && !pko_command.s.dontfree) + pko_command.s.dontfree = 1; + + if (pko_command.s.dontfree) { + queue_type = QUEUE_CORE; + pko_command.s.reg0 = priv->fau + qos * 4; + } else { + queue_type = QUEUE_HW; + } + if (USE_ASYNC_IOBDMA) + cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, + FAU_TOTAL_TX_TO_CLEAN, 1); + + spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); + + /* Drop this packet if we have too many already queued to the HW */ + if (unlikely(skb_queue_len(&priv->tx_free_list[qos]) >= + MAX_OUT_QUEUE_DEPTH)) { + if (dev->tx_queue_len != 0) { + /* Drop the lock when notifying the core. */ + spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, + flags); + netif_stop_queue(dev); + spin_lock_irqsave(&priv->tx_free_list[qos].lock, + flags); + } else { + /* If not using normal queueing. */ + queue_type = QUEUE_DROP; + goto skip_xmit; + } + } + + cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos, + CVMX_PKO_LOCK_NONE); + + /* Send the packet to the output queue */ + if (unlikely(cvmx_pko_send_packet_finish(priv->port, + priv->queue + qos, + pko_command, hw_buffer, + CVMX_PKO_LOCK_NONE))) { + printk_ratelimited("%s: Failed to send the packet\n", + dev->name); + queue_type = QUEUE_DROP; + } +skip_xmit: + to_free_list = NULL; + + switch (queue_type) { + case QUEUE_DROP: + skb->next = to_free_list; + to_free_list = skb; + dev->stats.tx_dropped++; + break; + case QUEUE_HW: + cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, -1); + break; + case QUEUE_CORE: + __skb_queue_tail(&priv->tx_free_list[qos], skb); + break; + default: + BUG(); + } + + while (skb_to_free > 0) { + struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]); + + t->next = to_free_list; + to_free_list = t; + skb_to_free--; + } + + spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags); + + /* Do the actual freeing outside of the lock. */ + while (to_free_list) { + struct sk_buff *t = to_free_list; + + to_free_list = to_free_list->next; + dev_kfree_skb_any(t); + } + + if (USE_ASYNC_IOBDMA) { + CVMX_SYNCIOBDMA; + total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH); + /* Restore the scratch area */ + cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch); + cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2); + } else { + total_to_clean = + cvmx_fau_fetch_and_add32(FAU_TOTAL_TX_TO_CLEAN, 1); + } + + if (total_to_clean & 0x3ff) { + /* + * Schedule the cleanup tasklet every 1024 packets for + * the pathological case of high traffic on one port + * delaying clean up of packets on a different port + * that is blocked waiting for the cleanup. + */ + tasklet_schedule(&cvm_oct_tx_cleanup_tasklet); + } + + cvm_oct_kick_tx_poll_watchdog(); + + return NETDEV_TX_OK; +} + +/** + * cvm_oct_xmit_pow - transmit a packet to the POW + * @skb: Packet to send + * @dev: Device info structure + + * Returns Always returns zero + */ +int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + void *packet_buffer; + void *copy_location; + + /* Get a work queue entry */ + struct cvmx_wqe *work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL); + + if (unlikely(!work)) { + printk_ratelimited("%s: Failed to allocate a work queue entry\n", + dev->name); + dev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + return 0; + } + + /* Get a packet buffer */ + packet_buffer = cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL); + if (unlikely(!packet_buffer)) { + printk_ratelimited("%s: Failed to allocate a packet buffer\n", + dev->name); + cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, 1); + dev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + return 0; + } + + /* + * Calculate where we need to copy the data to. We need to + * leave 8 bytes for a next pointer (unused). We also need to + * include any configure skip. Then we need to align the IP + * packet src and dest into the same 64bit word. The below + * calculation may add a little extra, but that doesn't + * hurt. + */ + copy_location = packet_buffer + sizeof(u64); + copy_location += ((CVMX_HELPER_FIRST_MBUFF_SKIP + 7) & 0xfff8) + 6; + + /* + * We have to copy the packet since whoever processes this + * packet will free it to a hardware pool. We can't use the + * trick of counting outstanding packets like in + * cvm_oct_xmit. + */ + memcpy(copy_location, skb->data, skb->len); + + /* + * Fill in some of the work queue fields. We may need to add + * more if the software at the other end needs them. + */ + if (!OCTEON_IS_MODEL(OCTEON_CN68XX)) + work->word0.pip.cn38xx.hw_chksum = skb->csum; + work->word1.len = skb->len; + cvmx_wqe_set_port(work, priv->port); + cvmx_wqe_set_qos(work, priv->port & 0x7); + cvmx_wqe_set_grp(work, pow_send_group); + work->word1.tag_type = CVMX_HELPER_INPUT_TAG_TYPE; + work->word1.tag = pow_send_group; /* FIXME */ + /* Default to zero. Sets of zero later are commented out */ + work->word2.u64 = 0; + work->word2.s.bufs = 1; + work->packet_ptr.u64 = 0; + work->packet_ptr.s.addr = cvmx_ptr_to_phys(copy_location); + work->packet_ptr.s.pool = CVMX_FPA_PACKET_POOL; + work->packet_ptr.s.size = CVMX_FPA_PACKET_POOL_SIZE; + work->packet_ptr.s.back = (copy_location - packet_buffer) >> 7; + + if (skb->protocol == htons(ETH_P_IP)) { + work->word2.s.ip_offset = 14; +#if 0 + work->word2.s.vlan_valid = 0; /* FIXME */ + work->word2.s.vlan_cfi = 0; /* FIXME */ + work->word2.s.vlan_id = 0; /* FIXME */ + work->word2.s.dec_ipcomp = 0; /* FIXME */ +#endif + work->word2.s.tcp_or_udp = + (ip_hdr(skb)->protocol == IPPROTO_TCP) || + (ip_hdr(skb)->protocol == IPPROTO_UDP); +#if 0 + /* FIXME */ + work->word2.s.dec_ipsec = 0; + /* We only support IPv4 right now */ + work->word2.s.is_v6 = 0; + /* Hardware would set to zero */ + work->word2.s.software = 0; + /* No error, packet is internal */ + work->word2.s.L4_error = 0; +#endif + work->word2.s.is_frag = !((ip_hdr(skb)->frag_off == 0) || + (ip_hdr(skb)->frag_off == + cpu_to_be16(1 << 14))); +#if 0 + /* Assume Linux is sending a good packet */ + work->word2.s.IP_exc = 0; +#endif + work->word2.s.is_bcast = (skb->pkt_type == PACKET_BROADCAST); + work->word2.s.is_mcast = (skb->pkt_type == PACKET_MULTICAST); +#if 0 + /* This is an IP packet */ + work->word2.s.not_IP = 0; + /* No error, packet is internal */ + work->word2.s.rcv_error = 0; + /* No error, packet is internal */ + work->word2.s.err_code = 0; +#endif + + /* + * When copying the data, include 4 bytes of the + * ethernet header to align the same way hardware + * does. + */ + memcpy(work->packet_data, skb->data + 10, + sizeof(work->packet_data)); + } else { +#if 0 + work->word2.snoip.vlan_valid = 0; /* FIXME */ + work->word2.snoip.vlan_cfi = 0; /* FIXME */ + work->word2.snoip.vlan_id = 0; /* FIXME */ + work->word2.snoip.software = 0; /* Hardware would set to zero */ +#endif + work->word2.snoip.is_rarp = skb->protocol == htons(ETH_P_RARP); + work->word2.snoip.is_arp = skb->protocol == htons(ETH_P_ARP); + work->word2.snoip.is_bcast = + (skb->pkt_type == PACKET_BROADCAST); + work->word2.snoip.is_mcast = + (skb->pkt_type == PACKET_MULTICAST); + work->word2.snoip.not_IP = 1; /* IP was done up above */ +#if 0 + /* No error, packet is internal */ + work->word2.snoip.rcv_error = 0; + /* No error, packet is internal */ + work->word2.snoip.err_code = 0; +#endif + memcpy(work->packet_data, skb->data, sizeof(work->packet_data)); + } + + /* Submit the packet to the POW */ + cvmx_pow_work_submit(work, work->word1.tag, work->word1.tag_type, + cvmx_wqe_get_qos(work), cvmx_wqe_get_grp(work)); + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + dev_consume_skb_any(skb); + return 0; +} + +/** + * cvm_oct_tx_shutdown_dev - free all skb that are currently queued for TX. + * @dev: Device being shutdown + * + */ +void cvm_oct_tx_shutdown_dev(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + unsigned long flags; + int qos; + + for (qos = 0; qos < 16; qos++) { + spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags); + while (skb_queue_len(&priv->tx_free_list[qos])) + dev_kfree_skb_any(__skb_dequeue + (&priv->tx_free_list[qos])); + spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags); + } +} + +static void cvm_oct_tx_do_cleanup(unsigned long arg) +{ + int port; + + for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { + if (cvm_oct_device[port]) { + struct net_device *dev = cvm_oct_device[port]; + + cvm_oct_free_tx_skbs(dev); + } + } +} + +static irqreturn_t cvm_oct_tx_cleanup_watchdog(int cpl, void *dev_id) +{ + /* Disable the interrupt. */ + cvmx_write_csr(CVMX_CIU_TIMX(1), 0); + /* Do the work in the tasklet. */ + tasklet_schedule(&cvm_oct_tx_cleanup_tasklet); + return IRQ_HANDLED; +} + +void cvm_oct_tx_initialize(void) +{ + int i; + + /* Disable the interrupt. */ + cvmx_write_csr(CVMX_CIU_TIMX(1), 0); + /* Register an IRQ handler to receive CIU_TIMX(1) interrupts */ + i = request_irq(OCTEON_IRQ_TIMER1, + cvm_oct_tx_cleanup_watchdog, 0, + "Ethernet", cvm_oct_device); + + if (i) + panic("Could not acquire Ethernet IRQ %d\n", OCTEON_IRQ_TIMER1); +} + +void cvm_oct_tx_shutdown(void) +{ + /* Free the interrupt handler */ + free_irq(OCTEON_IRQ_TIMER1, cvm_oct_device); +} diff --git a/drivers/staging/octeon/ethernet-tx.h b/drivers/staging/octeon/ethernet-tx.h new file mode 100644 index 000000000000..78936e9b33b0 --- /dev/null +++ b/drivers/staging/octeon/ethernet-tx.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev); +int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev); +int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry, + int do_free, int qos); +void cvm_oct_tx_initialize(void); +void cvm_oct_tx_shutdown(void); +void cvm_oct_tx_shutdown_dev(struct net_device *dev); diff --git a/drivers/staging/octeon/ethernet-util.h b/drivers/staging/octeon/ethernet-util.h new file mode 100644 index 000000000000..2af83a12ca78 --- /dev/null +++ b/drivers/staging/octeon/ethernet-util.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +/** + * cvm_oct_get_buffer_ptr - convert packet data address to pointer + * @packet_ptr: Packet data hardware address + * + * Returns Packet buffer pointer + */ +static inline void *cvm_oct_get_buffer_ptr(union cvmx_buf_ptr packet_ptr) +{ + return cvmx_phys_to_ptr(((packet_ptr.s.addr >> 7) - packet_ptr.s.back) + << 7); +} + +/** + * INTERFACE - convert IPD port to logical interface + * @ipd_port: Port to check + * + * Returns Logical interface + */ +static inline int INTERFACE(int ipd_port) +{ + int interface; + + if (ipd_port == CVMX_PIP_NUM_INPUT_PORTS) + return 10; + interface = cvmx_helper_get_interface_num(ipd_port); + if (interface >= 0) + return interface; + panic("Illegal ipd_port %d passed to %s\n", ipd_port, __func__); +} + +/** + * INDEX - convert IPD/PKO port number to the port's interface index + * @ipd_port: Port to check + * + * Returns Index into interface port list + */ +static inline int INDEX(int ipd_port) +{ + return cvmx_helper_get_interface_index_num(ipd_port); +} diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c new file mode 100644 index 000000000000..f42c3816ce49 --- /dev/null +++ b/drivers/staging/octeon/ethernet.c @@ -0,0 +1,992 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2007 Cavium Networks + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "octeon-ethernet.h" +#include "ethernet-defines.h" +#include "ethernet-mem.h" +#include "ethernet-rx.h" +#include "ethernet-tx.h" +#include "ethernet-mdio.h" +#include "ethernet-util.h" + +#define OCTEON_MAX_MTU 65392 + +static int num_packet_buffers = 1024; +module_param(num_packet_buffers, int, 0444); +MODULE_PARM_DESC(num_packet_buffers, "\n" + "\tNumber of packet buffers to allocate and store in the\n" + "\tFPA. By default, 1024 packet buffers are used.\n"); + +static int pow_receive_group = 15; +module_param(pow_receive_group, int, 0444); +MODULE_PARM_DESC(pow_receive_group, "\n" + "\tPOW group to receive packets from. All ethernet hardware\n" + "\twill be configured to send incoming packets to this POW\n" + "\tgroup. Also any other software can submit packets to this\n" + "\tgroup for the kernel to process."); + +static int receive_group_order; +module_param(receive_group_order, int, 0444); +MODULE_PARM_DESC(receive_group_order, "\n" + "\tOrder (0..4) of receive groups to take into use. Ethernet hardware\n" + "\twill be configured to send incoming packets to multiple POW\n" + "\tgroups. pow_receive_group parameter is ignored when multiple\n" + "\tgroups are taken into use and groups are allocated starting\n" + "\tfrom 0. By default, a single group is used.\n"); + +int pow_send_group = -1; +module_param(pow_send_group, int, 0644); +MODULE_PARM_DESC(pow_send_group, "\n" + "\tPOW group to send packets to other software on. This\n" + "\tcontrols the creation of the virtual device pow0.\n" + "\talways_use_pow also depends on this value."); + +int always_use_pow; +module_param(always_use_pow, int, 0444); +MODULE_PARM_DESC(always_use_pow, "\n" + "\tWhen set, always send to the pow group. This will cause\n" + "\tpackets sent to real ethernet devices to be sent to the\n" + "\tPOW group instead of the hardware. Unless some other\n" + "\tapplication changes the config, packets will still be\n" + "\treceived from the low level hardware. Use this option\n" + "\tto allow a CVMX app to intercept all packets from the\n" + "\tlinux kernel. You must specify pow_send_group along with\n" + "\tthis option."); + +char pow_send_list[128] = ""; +module_param_string(pow_send_list, pow_send_list, sizeof(pow_send_list), 0444); +MODULE_PARM_DESC(pow_send_list, "\n" + "\tComma separated list of ethernet devices that should use the\n" + "\tPOW for transmit instead of the actual ethernet hardware. This\n" + "\tis a per port version of always_use_pow. always_use_pow takes\n" + "\tprecedence over this list. For example, setting this to\n" + "\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n" + "\tusing the pow_send_group."); + +int rx_napi_weight = 32; +module_param(rx_napi_weight, int, 0444); +MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter."); + +/* Mask indicating which receive groups are in use. */ +int pow_receive_groups; + +/* + * cvm_oct_poll_queue_stopping - flag to indicate polling should stop. + * + * Set to one right before cvm_oct_poll_queue is destroyed. + */ +atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0); + +/* + * Array of every ethernet device owned by this driver indexed by + * the ipd input port number. + */ +struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS]; + +u64 cvm_oct_tx_poll_interval; + +static void cvm_oct_rx_refill_worker(struct work_struct *work); +static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker); + +static void cvm_oct_rx_refill_worker(struct work_struct *work) +{ + /* + * FPA 0 may have been drained, try to refill it if we need + * more than num_packet_buffers / 2, otherwise normal receive + * processing will refill it. If it were drained, no packets + * could be received so cvm_oct_napi_poll would never be + * invoked to do the refill. + */ + cvm_oct_rx_refill_pool(num_packet_buffers / 2); + + if (!atomic_read(&cvm_oct_poll_queue_stopping)) + schedule_delayed_work(&cvm_oct_rx_refill_work, HZ); +} + +static void cvm_oct_periodic_worker(struct work_struct *work) +{ + struct octeon_ethernet *priv = container_of(work, + struct octeon_ethernet, + port_periodic_work.work); + + if (priv->poll) + priv->poll(cvm_oct_device[priv->port]); + + cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats + (cvm_oct_device[priv->port]); + + if (!atomic_read(&cvm_oct_poll_queue_stopping)) + schedule_delayed_work(&priv->port_periodic_work, HZ); +} + +static void cvm_oct_configure_common_hw(void) +{ + /* Setup the FPA */ + cvmx_fpa_enable(); + cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, + num_packet_buffers); + cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, + num_packet_buffers); + if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) + cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, + CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 1024); + +#ifdef __LITTLE_ENDIAN + { + union cvmx_ipd_ctl_status ipd_ctl_status; + + ipd_ctl_status.u64 = cvmx_read_csr(CVMX_IPD_CTL_STATUS); + ipd_ctl_status.s.pkt_lend = 1; + ipd_ctl_status.s.wqe_lend = 1; + cvmx_write_csr(CVMX_IPD_CTL_STATUS, ipd_ctl_status.u64); + } +#endif + + cvmx_helper_setup_red(num_packet_buffers / 4, num_packet_buffers / 8); +} + +/** + * cvm_oct_free_work- Free a work queue entry + * + * @work_queue_entry: Work queue entry to free + * + * Returns Zero on success, Negative on failure. + */ +int cvm_oct_free_work(void *work_queue_entry) +{ + struct cvmx_wqe *work = work_queue_entry; + + int segments = work->word2.s.bufs; + union cvmx_buf_ptr segment_ptr = work->packet_ptr; + + while (segments--) { + union cvmx_buf_ptr next_ptr = *(union cvmx_buf_ptr *) + cvmx_phys_to_ptr(segment_ptr.s.addr - 8); + if (unlikely(!segment_ptr.s.i)) + cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), + segment_ptr.s.pool, + CVMX_FPA_PACKET_POOL_SIZE / 128); + segment_ptr = next_ptr; + } + cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, 1); + + return 0; +} +EXPORT_SYMBOL(cvm_oct_free_work); + +/** + * cvm_oct_common_get_stats - get the low level ethernet statistics + * @dev: Device to get the statistics from + * + * Returns Pointer to the statistics + */ +static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev) +{ + cvmx_pip_port_status_t rx_status; + cvmx_pko_port_status_t tx_status; + struct octeon_ethernet *priv = netdev_priv(dev); + + if (priv->port < CVMX_PIP_NUM_INPUT_PORTS) { + if (octeon_is_simulation()) { + /* The simulator doesn't support statistics */ + memset(&rx_status, 0, sizeof(rx_status)); + memset(&tx_status, 0, sizeof(tx_status)); + } else { + cvmx_pip_get_port_status(priv->port, 1, &rx_status); + cvmx_pko_get_port_status(priv->port, 1, &tx_status); + } + + dev->stats.rx_packets += rx_status.inb_packets; + dev->stats.tx_packets += tx_status.packets; + dev->stats.rx_bytes += rx_status.inb_octets; + dev->stats.tx_bytes += tx_status.octets; + dev->stats.multicast += rx_status.multicast_packets; + dev->stats.rx_crc_errors += rx_status.inb_errors; + dev->stats.rx_frame_errors += rx_status.fcs_align_err_packets; + dev->stats.rx_dropped += rx_status.dropped_packets; + } + + return &dev->stats; +} + +/** + * cvm_oct_common_change_mtu - change the link MTU + * @dev: Device to change + * @new_mtu: The new MTU + * + * Returns Zero on success + */ +static int cvm_oct_common_change_mtu(struct net_device *dev, int new_mtu) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + int interface = INTERFACE(priv->port); +#if IS_ENABLED(CONFIG_VLAN_8021Q) + int vlan_bytes = VLAN_HLEN; +#else + int vlan_bytes = 0; +#endif + int mtu_overhead = ETH_HLEN + ETH_FCS_LEN + vlan_bytes; + + dev->mtu = new_mtu; + + if ((interface < 2) && + (cvmx_helper_interface_get_mode(interface) != + CVMX_HELPER_INTERFACE_MODE_SPI)) { + int index = INDEX(priv->port); + /* Add ethernet header and FCS, and VLAN if configured. */ + int max_packet = new_mtu + mtu_overhead; + + if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || + OCTEON_IS_MODEL(OCTEON_CN58XX)) { + /* Signal errors on packets larger than the MTU */ + cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(index, interface), + max_packet); + } else { + /* + * Set the hardware to truncate packets larger + * than the MTU and smaller the 64 bytes. + */ + union cvmx_pip_frm_len_chkx frm_len_chk; + + frm_len_chk.u64 = 0; + frm_len_chk.s.minlen = VLAN_ETH_ZLEN; + frm_len_chk.s.maxlen = max_packet; + cvmx_write_csr(CVMX_PIP_FRM_LEN_CHKX(interface), + frm_len_chk.u64); + } + /* + * Set the hardware to truncate packets larger than + * the MTU. The jabber register must be set to a + * multiple of 8 bytes, so round up. + */ + cvmx_write_csr(CVMX_GMXX_RXX_JABBER(index, interface), + (max_packet + 7) & ~7u); + } + return 0; +} + +/** + * cvm_oct_common_set_multicast_list - set the multicast list + * @dev: Device to work on + */ +static void cvm_oct_common_set_multicast_list(struct net_device *dev) +{ + union cvmx_gmxx_prtx_cfg gmx_cfg; + struct octeon_ethernet *priv = netdev_priv(dev); + int interface = INTERFACE(priv->port); + + if ((interface < 2) && + (cvmx_helper_interface_get_mode(interface) != + CVMX_HELPER_INTERFACE_MODE_SPI)) { + union cvmx_gmxx_rxx_adr_ctl control; + int index = INDEX(priv->port); + + control.u64 = 0; + control.s.bcst = 1; /* Allow broadcast MAC addresses */ + + if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI) || + (dev->flags & IFF_PROMISC)) + /* Force accept multicast packets */ + control.s.mcst = 2; + else + /* Force reject multicast packets */ + control.s.mcst = 1; + + if (dev->flags & IFF_PROMISC) + /* + * Reject matches if promisc. Since CAM is + * shut off, should accept everything. + */ + control.s.cam_mode = 0; + else + /* Filter packets based on the CAM */ + control.s.cam_mode = 1; + + gmx_cfg.u64 = + cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), + gmx_cfg.u64 & ~1ull); + + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CTL(index, interface), + control.u64); + if (dev->flags & IFF_PROMISC) + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN + (index, interface), 0); + else + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN + (index, interface), 1); + + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), + gmx_cfg.u64); + } +} + +static int cvm_oct_set_mac_filter(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + union cvmx_gmxx_prtx_cfg gmx_cfg; + int interface = INTERFACE(priv->port); + + if ((interface < 2) && + (cvmx_helper_interface_get_mode(interface) != + CVMX_HELPER_INTERFACE_MODE_SPI)) { + int i; + u8 *ptr = dev->dev_addr; + u64 mac = 0; + int index = INDEX(priv->port); + + for (i = 0; i < 6; i++) + mac = (mac << 8) | (u64)ptr[i]; + + gmx_cfg.u64 = + cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), + gmx_cfg.u64 & ~1ull); + + cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac); + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface), + ptr[0]); + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface), + ptr[1]); + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface), + ptr[2]); + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface), + ptr[3]); + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface), + ptr[4]); + cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface), + ptr[5]); + cvm_oct_common_set_multicast_list(dev); + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), + gmx_cfg.u64); + } + return 0; +} + +/** + * cvm_oct_common_set_mac_address - set the hardware MAC address for a device + * @dev: The device in question. + * @addr: Socket address. + * + * Returns Zero on success + */ +static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr) +{ + int r = eth_mac_addr(dev, addr); + + if (r) + return r; + return cvm_oct_set_mac_filter(dev); +} + +/** + * cvm_oct_common_init - per network device initialization + * @dev: Device to initialize + * + * Returns Zero on success + */ +int cvm_oct_common_init(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + const u8 *mac = NULL; + + if (priv->of_node) + mac = of_get_mac_address(priv->of_node); + + if (!IS_ERR_OR_NULL(mac)) + ether_addr_copy(dev->dev_addr, mac); + else + eth_hw_addr_random(dev); + + /* + * Force the interface to use the POW send if always_use_pow + * was specified or it is in the pow send list. + */ + if ((pow_send_group != -1) && + (always_use_pow || strstr(pow_send_list, dev->name))) + priv->queue = -1; + + if (priv->queue != -1) + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; + + /* We do our own locking, Linux doesn't need to */ + dev->features |= NETIF_F_LLTX; + dev->ethtool_ops = &cvm_oct_ethtool_ops; + + cvm_oct_set_mac_filter(dev); + dev_set_mtu(dev, dev->mtu); + + /* + * Zero out stats for port so we won't mistakenly show + * counters from the bootloader. + */ + memset(dev->netdev_ops->ndo_get_stats(dev), 0, + sizeof(struct net_device_stats)); + + if (dev->netdev_ops->ndo_stop) + dev->netdev_ops->ndo_stop(dev); + + return 0; +} + +void cvm_oct_common_uninit(struct net_device *dev) +{ + if (dev->phydev) + phy_disconnect(dev->phydev); +} + +int cvm_oct_common_open(struct net_device *dev, + void (*link_poll)(struct net_device *)) +{ + union cvmx_gmxx_prtx_cfg gmx_cfg; + struct octeon_ethernet *priv = netdev_priv(dev); + int interface = INTERFACE(priv->port); + int index = INDEX(priv->port); + union cvmx_helper_link_info link_info; + int rv; + + rv = cvm_oct_phy_setup_device(dev); + if (rv) + return rv; + + gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); + gmx_cfg.s.en = 1; + if (octeon_has_feature(OCTEON_FEATURE_PKND)) + gmx_cfg.s.pknd = priv->port; + cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); + + if (octeon_is_simulation()) + return 0; + + if (dev->phydev) { + int r = phy_read_status(dev->phydev); + + if (r == 0 && dev->phydev->link == 0) + netif_carrier_off(dev); + cvm_oct_adjust_link(dev); + } else { + link_info = cvmx_helper_link_get(priv->port); + if (!link_info.s.link_up) + netif_carrier_off(dev); + priv->poll = link_poll; + link_poll(dev); + } + + return 0; +} + +void cvm_oct_link_poll(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + union cvmx_helper_link_info link_info; + + link_info = cvmx_helper_link_get(priv->port); + if (link_info.u64 == priv->link_info) + return; + + if (cvmx_helper_link_set(priv->port, link_info)) + link_info.u64 = priv->link_info; + else + priv->link_info = link_info.u64; + + if (link_info.s.link_up) { + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + } else if (netif_carrier_ok(dev)) { + netif_carrier_off(dev); + } + cvm_oct_note_carrier(priv, link_info); +} + +static int cvm_oct_xaui_open(struct net_device *dev) +{ + return cvm_oct_common_open(dev, cvm_oct_link_poll); +} + +static const struct net_device_ops cvm_oct_npi_netdev_ops = { + .ndo_init = cvm_oct_common_init, + .ndo_uninit = cvm_oct_common_uninit, + .ndo_start_xmit = cvm_oct_xmit, + .ndo_set_rx_mode = cvm_oct_common_set_multicast_list, + .ndo_set_mac_address = cvm_oct_common_set_mac_address, + .ndo_do_ioctl = cvm_oct_ioctl, + .ndo_change_mtu = cvm_oct_common_change_mtu, + .ndo_get_stats = cvm_oct_common_get_stats, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cvm_oct_poll_controller, +#endif +}; + +static const struct net_device_ops cvm_oct_xaui_netdev_ops = { + .ndo_init = cvm_oct_common_init, + .ndo_uninit = cvm_oct_common_uninit, + .ndo_open = cvm_oct_xaui_open, + .ndo_stop = cvm_oct_common_stop, + .ndo_start_xmit = cvm_oct_xmit, + .ndo_set_rx_mode = cvm_oct_common_set_multicast_list, + .ndo_set_mac_address = cvm_oct_common_set_mac_address, + .ndo_do_ioctl = cvm_oct_ioctl, + .ndo_change_mtu = cvm_oct_common_change_mtu, + .ndo_get_stats = cvm_oct_common_get_stats, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cvm_oct_poll_controller, +#endif +}; + +static const struct net_device_ops cvm_oct_sgmii_netdev_ops = { + .ndo_init = cvm_oct_sgmii_init, + .ndo_uninit = cvm_oct_common_uninit, + .ndo_open = cvm_oct_sgmii_open, + .ndo_stop = cvm_oct_common_stop, + .ndo_start_xmit = cvm_oct_xmit, + .ndo_set_rx_mode = cvm_oct_common_set_multicast_list, + .ndo_set_mac_address = cvm_oct_common_set_mac_address, + .ndo_do_ioctl = cvm_oct_ioctl, + .ndo_change_mtu = cvm_oct_common_change_mtu, + .ndo_get_stats = cvm_oct_common_get_stats, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cvm_oct_poll_controller, +#endif +}; + +static const struct net_device_ops cvm_oct_spi_netdev_ops = { + .ndo_init = cvm_oct_spi_init, + .ndo_uninit = cvm_oct_spi_uninit, + .ndo_start_xmit = cvm_oct_xmit, + .ndo_set_rx_mode = cvm_oct_common_set_multicast_list, + .ndo_set_mac_address = cvm_oct_common_set_mac_address, + .ndo_do_ioctl = cvm_oct_ioctl, + .ndo_change_mtu = cvm_oct_common_change_mtu, + .ndo_get_stats = cvm_oct_common_get_stats, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cvm_oct_poll_controller, +#endif +}; + +static const struct net_device_ops cvm_oct_rgmii_netdev_ops = { + .ndo_init = cvm_oct_common_init, + .ndo_uninit = cvm_oct_common_uninit, + .ndo_open = cvm_oct_rgmii_open, + .ndo_stop = cvm_oct_common_stop, + .ndo_start_xmit = cvm_oct_xmit, + .ndo_set_rx_mode = cvm_oct_common_set_multicast_list, + .ndo_set_mac_address = cvm_oct_common_set_mac_address, + .ndo_do_ioctl = cvm_oct_ioctl, + .ndo_change_mtu = cvm_oct_common_change_mtu, + .ndo_get_stats = cvm_oct_common_get_stats, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cvm_oct_poll_controller, +#endif +}; + +static const struct net_device_ops cvm_oct_pow_netdev_ops = { + .ndo_init = cvm_oct_common_init, + .ndo_start_xmit = cvm_oct_xmit_pow, + .ndo_set_rx_mode = cvm_oct_common_set_multicast_list, + .ndo_set_mac_address = cvm_oct_common_set_mac_address, + .ndo_do_ioctl = cvm_oct_ioctl, + .ndo_change_mtu = cvm_oct_common_change_mtu, + .ndo_get_stats = cvm_oct_common_get_stats, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cvm_oct_poll_controller, +#endif +}; + +static struct device_node *cvm_oct_of_get_child + (const struct device_node *parent, int reg_val) +{ + struct device_node *node = NULL; + int size; + const __be32 *addr; + + for (;;) { + node = of_get_next_child(parent, node); + if (!node) + break; + addr = of_get_property(node, "reg", &size); + if (addr && (be32_to_cpu(*addr) == reg_val)) + break; + } + return node; +} + +static struct device_node *cvm_oct_node_for_port(struct device_node *pip, + int interface, int port) +{ + struct device_node *ni, *np; + + ni = cvm_oct_of_get_child(pip, interface); + if (!ni) + return NULL; + + np = cvm_oct_of_get_child(ni, port); + of_node_put(ni); + + return np; +} + +static void cvm_set_rgmii_delay(struct octeon_ethernet *priv, int iface, + int port) +{ + struct device_node *np = priv->of_node; + u32 delay_value; + bool rx_delay; + bool tx_delay; + + /* By default, both RX/TX delay is enabled in + * __cvmx_helper_rgmii_enable(). + */ + rx_delay = true; + tx_delay = true; + + if (!of_property_read_u32(np, "rx-delay", &delay_value)) { + cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, iface), delay_value); + rx_delay = delay_value > 0; + } + if (!of_property_read_u32(np, "tx-delay", &delay_value)) { + cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, iface), delay_value); + tx_delay = delay_value > 0; + } + + if (!rx_delay && !tx_delay) + priv->phy_mode = PHY_INTERFACE_MODE_RGMII_ID; + else if (!rx_delay) + priv->phy_mode = PHY_INTERFACE_MODE_RGMII_RXID; + else if (!tx_delay) + priv->phy_mode = PHY_INTERFACE_MODE_RGMII_TXID; + else + priv->phy_mode = PHY_INTERFACE_MODE_RGMII; +} + +static int cvm_oct_probe(struct platform_device *pdev) +{ + int num_interfaces; + int interface; + int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE; + int qos; + struct device_node *pip; + int mtu_overhead = ETH_HLEN + ETH_FCS_LEN; + +#if IS_ENABLED(CONFIG_VLAN_8021Q) + mtu_overhead += VLAN_HLEN; +#endif + + octeon_mdiobus_force_mod_depencency(); + + pip = pdev->dev.of_node; + if (!pip) { + pr_err("Error: No 'pip' in /aliases\n"); + return -EINVAL; + } + + cvm_oct_configure_common_hw(); + + cvmx_helper_initialize_packet_io_global(); + + if (receive_group_order) { + if (receive_group_order > 4) + receive_group_order = 4; + pow_receive_groups = (1 << (1 << receive_group_order)) - 1; + } else { + pow_receive_groups = BIT(pow_receive_group); + } + + /* Change the input group for all ports before input is enabled */ + num_interfaces = cvmx_helper_get_number_of_interfaces(); + for (interface = 0; interface < num_interfaces; interface++) { + int num_ports = cvmx_helper_ports_on_interface(interface); + int port; + + for (port = cvmx_helper_get_ipd_port(interface, 0); + port < cvmx_helper_get_ipd_port(interface, num_ports); + port++) { + union cvmx_pip_prt_tagx pip_prt_tagx; + + pip_prt_tagx.u64 = + cvmx_read_csr(CVMX_PIP_PRT_TAGX(port)); + + if (receive_group_order) { + int tag_mask; + + /* We support only 16 groups at the moment, so + * always disable the two additional "hidden" + * tag_mask bits on CN68XX. + */ + if (OCTEON_IS_MODEL(OCTEON_CN68XX)) + pip_prt_tagx.u64 |= 0x3ull << 44; + + tag_mask = ~((1 << receive_group_order) - 1); + pip_prt_tagx.s.grptagbase = 0; + pip_prt_tagx.s.grptagmask = tag_mask; + pip_prt_tagx.s.grptag = 1; + pip_prt_tagx.s.tag_mode = 0; + pip_prt_tagx.s.inc_prt_flag = 1; + pip_prt_tagx.s.ip6_dprt_flag = 1; + pip_prt_tagx.s.ip4_dprt_flag = 1; + pip_prt_tagx.s.ip6_sprt_flag = 1; + pip_prt_tagx.s.ip4_sprt_flag = 1; + pip_prt_tagx.s.ip6_dst_flag = 1; + pip_prt_tagx.s.ip4_dst_flag = 1; + pip_prt_tagx.s.ip6_src_flag = 1; + pip_prt_tagx.s.ip4_src_flag = 1; + pip_prt_tagx.s.grp = 0; + } else { + pip_prt_tagx.s.grptag = 0; + pip_prt_tagx.s.grp = pow_receive_group; + } + + cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), + pip_prt_tagx.u64); + } + } + + cvmx_helper_ipd_and_packet_input_enable(); + + memset(cvm_oct_device, 0, sizeof(cvm_oct_device)); + + /* + * Initialize the FAU used for counting packet buffers that + * need to be freed. + */ + cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0); + + /* Initialize the FAU used for counting tx SKBs that need to be freed */ + cvmx_fau_atomic_write32(FAU_TOTAL_TX_TO_CLEAN, 0); + + if ((pow_send_group != -1)) { + struct net_device *dev; + + dev = alloc_etherdev(sizeof(struct octeon_ethernet)); + if (dev) { + /* Initialize the device private structure. */ + struct octeon_ethernet *priv = netdev_priv(dev); + + SET_NETDEV_DEV(dev, &pdev->dev); + dev->netdev_ops = &cvm_oct_pow_netdev_ops; + priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED; + priv->port = CVMX_PIP_NUM_INPUT_PORTS; + priv->queue = -1; + strscpy(dev->name, "pow%d", sizeof(dev->name)); + for (qos = 0; qos < 16; qos++) + skb_queue_head_init(&priv->tx_free_list[qos]); + dev->min_mtu = VLAN_ETH_ZLEN - mtu_overhead; + dev->max_mtu = OCTEON_MAX_MTU - mtu_overhead; + + if (register_netdev(dev) < 0) { + pr_err("Failed to register ethernet device for POW\n"); + free_netdev(dev); + } else { + cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = dev; + pr_info("%s: POW send group %d, receive group %d\n", + dev->name, pow_send_group, + pow_receive_group); + } + } else { + pr_err("Failed to allocate ethernet device for POW\n"); + } + } + + num_interfaces = cvmx_helper_get_number_of_interfaces(); + for (interface = 0; interface < num_interfaces; interface++) { + cvmx_helper_interface_mode_t imode = + cvmx_helper_interface_get_mode(interface); + int num_ports = cvmx_helper_ports_on_interface(interface); + int port; + int port_index; + + for (port_index = 0, + port = cvmx_helper_get_ipd_port(interface, 0); + port < cvmx_helper_get_ipd_port(interface, num_ports); + port_index++, port++) { + struct octeon_ethernet *priv; + struct net_device *dev = + alloc_etherdev(sizeof(struct octeon_ethernet)); + if (!dev) { + pr_err("Failed to allocate ethernet device for port %d\n", + port); + continue; + } + + /* Initialize the device private structure. */ + SET_NETDEV_DEV(dev, &pdev->dev); + priv = netdev_priv(dev); + priv->netdev = dev; + priv->of_node = cvm_oct_node_for_port(pip, interface, + port_index); + + INIT_DELAYED_WORK(&priv->port_periodic_work, + cvm_oct_periodic_worker); + priv->imode = imode; + priv->port = port; + priv->queue = cvmx_pko_get_base_queue(priv->port); + priv->fau = fau - cvmx_pko_get_num_queues(port) * 4; + priv->phy_mode = PHY_INTERFACE_MODE_NA; + for (qos = 0; qos < 16; qos++) + skb_queue_head_init(&priv->tx_free_list[qos]); + for (qos = 0; qos < cvmx_pko_get_num_queues(port); + qos++) + cvmx_fau_atomic_write32(priv->fau + qos * 4, 0); + dev->min_mtu = VLAN_ETH_ZLEN - mtu_overhead; + dev->max_mtu = OCTEON_MAX_MTU - mtu_overhead; + + switch (priv->imode) { + /* These types don't support ports to IPD/PKO */ + case CVMX_HELPER_INTERFACE_MODE_DISABLED: + case CVMX_HELPER_INTERFACE_MODE_PCIE: + case CVMX_HELPER_INTERFACE_MODE_PICMG: + break; + + case CVMX_HELPER_INTERFACE_MODE_NPI: + dev->netdev_ops = &cvm_oct_npi_netdev_ops; + strscpy(dev->name, "npi%d", sizeof(dev->name)); + break; + + case CVMX_HELPER_INTERFACE_MODE_XAUI: + dev->netdev_ops = &cvm_oct_xaui_netdev_ops; + strscpy(dev->name, "xaui%d", sizeof(dev->name)); + break; + + case CVMX_HELPER_INTERFACE_MODE_LOOP: + dev->netdev_ops = &cvm_oct_npi_netdev_ops; + strscpy(dev->name, "loop%d", sizeof(dev->name)); + break; + + case CVMX_HELPER_INTERFACE_MODE_SGMII: + priv->phy_mode = PHY_INTERFACE_MODE_SGMII; + dev->netdev_ops = &cvm_oct_sgmii_netdev_ops; + strscpy(dev->name, "eth%d", sizeof(dev->name)); + break; + + case CVMX_HELPER_INTERFACE_MODE_SPI: + dev->netdev_ops = &cvm_oct_spi_netdev_ops; + strscpy(dev->name, "spi%d", sizeof(dev->name)); + break; + + case CVMX_HELPER_INTERFACE_MODE_GMII: + priv->phy_mode = PHY_INTERFACE_MODE_GMII; + dev->netdev_ops = &cvm_oct_rgmii_netdev_ops; + strscpy(dev->name, "eth%d", sizeof(dev->name)); + break; + + case CVMX_HELPER_INTERFACE_MODE_RGMII: + dev->netdev_ops = &cvm_oct_rgmii_netdev_ops; + strscpy(dev->name, "eth%d", sizeof(dev->name)); + cvm_set_rgmii_delay(priv, interface, + port_index); + break; + } + + if (!dev->netdev_ops) { + free_netdev(dev); + } else if (register_netdev(dev) < 0) { + pr_err("Failed to register ethernet device for interface %d, port %d\n", + interface, priv->port); + free_netdev(dev); + } else { + cvm_oct_device[priv->port] = dev; + fau -= + cvmx_pko_get_num_queues(priv->port) * + sizeof(u32); + schedule_delayed_work(&priv->port_periodic_work, + HZ); + } + } + } + + cvm_oct_tx_initialize(); + cvm_oct_rx_initialize(); + + /* + * 150 uS: about 10 1500-byte packets at 1GE. + */ + cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000); + + schedule_delayed_work(&cvm_oct_rx_refill_work, HZ); + + return 0; +} + +static int cvm_oct_remove(struct platform_device *pdev) +{ + int port; + + cvmx_ipd_disable(); + + atomic_inc_return(&cvm_oct_poll_queue_stopping); + cancel_delayed_work_sync(&cvm_oct_rx_refill_work); + + cvm_oct_rx_shutdown(); + cvm_oct_tx_shutdown(); + + cvmx_pko_disable(); + + /* Free the ethernet devices */ + for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) { + if (cvm_oct_device[port]) { + struct net_device *dev = cvm_oct_device[port]; + struct octeon_ethernet *priv = netdev_priv(dev); + + cancel_delayed_work_sync(&priv->port_periodic_work); + + cvm_oct_tx_shutdown_dev(dev); + unregister_netdev(dev); + free_netdev(dev); + cvm_oct_device[port] = NULL; + } + } + + cvmx_pko_shutdown(); + + cvmx_ipd_free_ptr(); + + /* Free the HW pools */ + cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, + num_packet_buffers); + cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, + num_packet_buffers); + if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL) + cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, + CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128); + return 0; +} + +static const struct of_device_id cvm_oct_match[] = { + { + .compatible = "cavium,octeon-3860-pip", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, cvm_oct_match); + +static struct platform_driver cvm_oct_driver = { + .probe = cvm_oct_probe, + .remove = cvm_oct_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = cvm_oct_match, + }, +}; + +module_platform_driver(cvm_oct_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cavium Networks "); +MODULE_DESCRIPTION("Cavium Networks Octeon ethernet driver."); diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h new file mode 100644 index 000000000000..a6140705706f --- /dev/null +++ b/drivers/staging/octeon/octeon-ethernet.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2010 Cavium Networks + */ + +/* + * External interface for the Cavium Octeon ethernet driver. + */ +#ifndef OCTEON_ETHERNET_H +#define OCTEON_ETHERNET_H + +#include +#include + +#ifdef CONFIG_CAVIUM_OCTEON_SOC + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include "octeon-stubs.h" + +#endif + +/** + * This is the definition of the Ethernet driver's private + * driver state stored in netdev_priv(dev). + */ +struct octeon_ethernet { + /* PKO hardware output port */ + int port; + /* PKO hardware queue for the port */ + int queue; + /* Hardware fetch and add to count outstanding tx buffers */ + int fau; + /* My netdev. */ + struct net_device *netdev; + /* + * Type of port. This is one of the enums in + * cvmx_helper_interface_mode_t + */ + int imode; + /* PHY mode */ + phy_interface_t phy_mode; + /* List of outstanding tx buffers per queue */ + struct sk_buff_head tx_free_list[16]; + unsigned int last_speed; + unsigned int last_link; + /* Last negotiated link state */ + u64 link_info; + /* Called periodically to check link status */ + void (*poll)(struct net_device *dev); + struct delayed_work port_periodic_work; + struct device_node *of_node; +}; + +int cvm_oct_free_work(void *work_queue_entry); + +int cvm_oct_rgmii_open(struct net_device *dev); + +int cvm_oct_sgmii_init(struct net_device *dev); +int cvm_oct_sgmii_open(struct net_device *dev); + +int cvm_oct_spi_init(struct net_device *dev); +void cvm_oct_spi_uninit(struct net_device *dev); + +int cvm_oct_common_init(struct net_device *dev); +void cvm_oct_common_uninit(struct net_device *dev); +void cvm_oct_adjust_link(struct net_device *dev); +int cvm_oct_common_stop(struct net_device *dev); +int cvm_oct_common_open(struct net_device *dev, + void (*link_poll)(struct net_device *)); +void cvm_oct_note_carrier(struct octeon_ethernet *priv, + union cvmx_helper_link_info li); +void cvm_oct_link_poll(struct net_device *dev); + +extern int always_use_pow; +extern int pow_send_group; +extern int pow_receive_groups; +extern char pow_send_list[]; +extern struct net_device *cvm_oct_device[]; +extern atomic_t cvm_oct_poll_queue_stopping; +extern u64 cvm_oct_tx_poll_interval; + +extern int rx_napi_weight; + +#endif diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h new file mode 100644 index 000000000000..79213c045504 --- /dev/null +++ b/drivers/staging/octeon/octeon-stubs.h @@ -0,0 +1,1433 @@ +#define CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE 512 + +#ifndef XKPHYS_TO_PHYS +# define XKPHYS_TO_PHYS(p) (p) +#endif + +#define OCTEON_IRQ_WORKQ0 0 +#define OCTEON_IRQ_RML 0 +#define OCTEON_IRQ_TIMER1 0 +#define OCTEON_IS_MODEL(x) 0 +#define octeon_has_feature(x) 0 +#define octeon_get_clock_rate() 0 + +#define CVMX_SYNCIOBDMA do { } while(0) + +#define CVMX_HELPER_INPUT_TAG_TYPE 0 +#define CVMX_HELPER_FIRST_MBUFF_SKIP 7 +#define CVMX_FAU_REG_END (2048) +#define CVMX_FPA_OUTPUT_BUFFER_POOL (2) +#define CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE 16 +#define CVMX_FPA_PACKET_POOL (0) +#define CVMX_FPA_PACKET_POOL_SIZE 16 +#define CVMX_FPA_WQE_POOL (1) +#define CVMX_FPA_WQE_POOL_SIZE 16 +#define CVMX_GMXX_RXX_ADR_CAM_EN(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_ADR_CTL(a, b) ((a)+(b)) +#define CVMX_GMXX_PRTX_CFG(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_FRM_MAX(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_JABBER(a, b) ((a)+(b)) +#define CVMX_IPD_CTL_STATUS 0 +#define CVMX_PIP_FRM_LEN_CHKX(a) (a) +#define CVMX_PIP_NUM_INPUT_PORTS 1 +#define CVMX_SCR_SCRATCH 0 +#define CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 2 +#define CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 2 +#define CVMX_IPD_SUB_PORT_FCS 0 +#define CVMX_SSO_WQ_IQ_DIS 0 +#define CVMX_SSO_WQ_INT 0 +#define CVMX_POW_WQ_INT 0 +#define CVMX_SSO_WQ_INT_PC 0 +#define CVMX_NPI_RSL_INT_BLOCKS 0 +#define CVMX_POW_WQ_INT_PC 0 + +union cvmx_pip_wqe_word2 { + uint64_t u64; + struct { + uint64_t bufs:8; + uint64_t ip_offset:8; + uint64_t vlan_valid:1; + uint64_t vlan_stacked:1; + uint64_t unassigned:1; + uint64_t vlan_cfi:1; + uint64_t vlan_id:12; + uint64_t pr:4; + uint64_t unassigned2:8; + uint64_t dec_ipcomp:1; + uint64_t tcp_or_udp:1; + uint64_t dec_ipsec:1; + uint64_t is_v6:1; + uint64_t software:1; + uint64_t L4_error:1; + uint64_t is_frag:1; + uint64_t IP_exc:1; + uint64_t is_bcast:1; + uint64_t is_mcast:1; + uint64_t not_IP:1; + uint64_t rcv_error:1; + uint64_t err_code:8; + } s; + struct { + uint64_t bufs:8; + uint64_t ip_offset:8; + uint64_t vlan_valid:1; + uint64_t vlan_stacked:1; + uint64_t unassigned:1; + uint64_t vlan_cfi:1; + uint64_t vlan_id:12; + uint64_t port:12; + uint64_t dec_ipcomp:1; + uint64_t tcp_or_udp:1; + uint64_t dec_ipsec:1; + uint64_t is_v6:1; + uint64_t software:1; + uint64_t L4_error:1; + uint64_t is_frag:1; + uint64_t IP_exc:1; + uint64_t is_bcast:1; + uint64_t is_mcast:1; + uint64_t not_IP:1; + uint64_t rcv_error:1; + uint64_t err_code:8; + } s_cn68xx; + + struct { + uint64_t unused1:16; + uint64_t vlan:16; + uint64_t unused2:32; + } svlan; + struct { + uint64_t bufs:8; + uint64_t unused:8; + uint64_t vlan_valid:1; + uint64_t vlan_stacked:1; + uint64_t unassigned:1; + uint64_t vlan_cfi:1; + uint64_t vlan_id:12; + uint64_t pr:4; + uint64_t unassigned2:12; + uint64_t software:1; + uint64_t unassigned3:1; + uint64_t is_rarp:1; + uint64_t is_arp:1; + uint64_t is_bcast:1; + uint64_t is_mcast:1; + uint64_t not_IP:1; + uint64_t rcv_error:1; + uint64_t err_code:8; + } snoip; + +}; + +union cvmx_pip_wqe_word0 { + struct { + uint64_t next_ptr:40; + uint8_t unused; + __wsum hw_chksum; + } cn38xx; + struct { + uint64_t pknd:6; /* 0..5 */ + uint64_t unused2:2; /* 6..7 */ + uint64_t bpid:6; /* 8..13 */ + uint64_t unused1:18; /* 14..31 */ + uint64_t l2ptr:8; /* 32..39 */ + uint64_t l3ptr:8; /* 40..47 */ + uint64_t unused0:8; /* 48..55 */ + uint64_t l4ptr:8; /* 56..63 */ + } cn68xx; +}; + +union cvmx_wqe_word0 { + uint64_t u64; + union cvmx_pip_wqe_word0 pip; +}; + +union cvmx_wqe_word1 { + uint64_t u64; + struct { + uint64_t tag:32; + uint64_t tag_type:2; + uint64_t varies:14; + uint64_t len:16; + }; + struct { + uint64_t tag:32; + uint64_t tag_type:2; + uint64_t zero_2:3; + uint64_t grp:6; + uint64_t zero_1:1; + uint64_t qos:3; + uint64_t zero_0:1; + uint64_t len:16; + } cn68xx; + struct { + uint64_t tag:32; + uint64_t tag_type:2; + uint64_t zero_2:1; + uint64_t grp:4; + uint64_t qos:3; + uint64_t ipprt:6; + uint64_t len:16; + } cn38xx; +}; + +union cvmx_buf_ptr { + void *ptr; + uint64_t u64; + struct { + uint64_t i:1; + uint64_t back:4; + uint64_t pool:3; + uint64_t size:16; + uint64_t addr:40; + } s; +}; + +struct cvmx_wqe { + union cvmx_wqe_word0 word0; + union cvmx_wqe_word1 word1; + union cvmx_pip_wqe_word2 word2; + union cvmx_buf_ptr packet_ptr; + uint8_t packet_data[96]; +}; + +union cvmx_helper_link_info { + uint64_t u64; + struct { + uint64_t reserved_20_63:44; + uint64_t link_up:1; /**< Is the physical link up? */ + uint64_t full_duplex:1; /**< 1 if the link is full duplex */ + uint64_t speed:18; /**< Speed of the link in Mbps */ + } s; +}; + +enum cvmx_fau_reg_32 { + CVMX_FAU_REG_32_START = 0, +}; + +enum cvmx_fau_op_size { + CVMX_FAU_OP_SIZE_8 = 0, + CVMX_FAU_OP_SIZE_16 = 1, + CVMX_FAU_OP_SIZE_32 = 2, + CVMX_FAU_OP_SIZE_64 = 3 +}; + +typedef enum { + CVMX_SPI_MODE_UNKNOWN = 0, + CVMX_SPI_MODE_TX_HALFPLEX = 1, + CVMX_SPI_MODE_RX_HALFPLEX = 2, + CVMX_SPI_MODE_DUPLEX = 3 +} cvmx_spi_mode_t; + +typedef enum { + CVMX_HELPER_INTERFACE_MODE_DISABLED, + CVMX_HELPER_INTERFACE_MODE_RGMII, + CVMX_HELPER_INTERFACE_MODE_GMII, + CVMX_HELPER_INTERFACE_MODE_SPI, + CVMX_HELPER_INTERFACE_MODE_PCIE, + CVMX_HELPER_INTERFACE_MODE_XAUI, + CVMX_HELPER_INTERFACE_MODE_SGMII, + CVMX_HELPER_INTERFACE_MODE_PICMG, + CVMX_HELPER_INTERFACE_MODE_NPI, + CVMX_HELPER_INTERFACE_MODE_LOOP, +} cvmx_helper_interface_mode_t; + +typedef enum { + CVMX_POW_WAIT = 1, + CVMX_POW_NO_WAIT = 0, +} cvmx_pow_wait_t; + +typedef enum { + CVMX_PKO_LOCK_NONE = 0, + CVMX_PKO_LOCK_ATOMIC_TAG = 1, + CVMX_PKO_LOCK_CMD_QUEUE = 2, +} cvmx_pko_lock_t; + +typedef enum { + CVMX_PKO_SUCCESS, + CVMX_PKO_INVALID_PORT, + CVMX_PKO_INVALID_QUEUE, + CVMX_PKO_INVALID_PRIORITY, + CVMX_PKO_NO_MEMORY, + CVMX_PKO_PORT_ALREADY_SETUP, + CVMX_PKO_CMD_QUEUE_INIT_ERROR +} cvmx_pko_status_t; + +enum cvmx_pow_tag_type { + CVMX_POW_TAG_TYPE_ORDERED = 0L, + CVMX_POW_TAG_TYPE_ATOMIC = 1L, + CVMX_POW_TAG_TYPE_NULL = 2L, + CVMX_POW_TAG_TYPE_NULL_NULL = 3L +}; + +union cvmx_ipd_ctl_status { + uint64_t u64; + struct cvmx_ipd_ctl_status_s { + uint64_t reserved_18_63:46; + uint64_t use_sop:1; + uint64_t rst_done:1; + uint64_t clken:1; + uint64_t no_wptr:1; + uint64_t pq_apkt:1; + uint64_t pq_nabuf:1; + uint64_t ipd_full:1; + uint64_t pkt_off:1; + uint64_t len_m8:1; + uint64_t reset:1; + uint64_t addpkt:1; + uint64_t naddbuf:1; + uint64_t pkt_lend:1; + uint64_t wqe_lend:1; + uint64_t pbp_en:1; + uint64_t opc_mode:2; + uint64_t ipd_en:1; + } s; + struct cvmx_ipd_ctl_status_cn30xx { + uint64_t reserved_10_63:54; + uint64_t len_m8:1; + uint64_t reset:1; + uint64_t addpkt:1; + uint64_t naddbuf:1; + uint64_t pkt_lend:1; + uint64_t wqe_lend:1; + uint64_t pbp_en:1; + uint64_t opc_mode:2; + uint64_t ipd_en:1; + } cn30xx; + struct cvmx_ipd_ctl_status_cn38xxp2 { + uint64_t reserved_9_63:55; + uint64_t reset:1; + uint64_t addpkt:1; + uint64_t naddbuf:1; + uint64_t pkt_lend:1; + uint64_t wqe_lend:1; + uint64_t pbp_en:1; + uint64_t opc_mode:2; + uint64_t ipd_en:1; + } cn38xxp2; + struct cvmx_ipd_ctl_status_cn50xx { + uint64_t reserved_15_63:49; + uint64_t no_wptr:1; + uint64_t pq_apkt:1; + uint64_t pq_nabuf:1; + uint64_t ipd_full:1; + uint64_t pkt_off:1; + uint64_t len_m8:1; + uint64_t reset:1; + uint64_t addpkt:1; + uint64_t naddbuf:1; + uint64_t pkt_lend:1; + uint64_t wqe_lend:1; + uint64_t pbp_en:1; + uint64_t opc_mode:2; + uint64_t ipd_en:1; + } cn50xx; + struct cvmx_ipd_ctl_status_cn58xx { + uint64_t reserved_12_63:52; + uint64_t ipd_full:1; + uint64_t pkt_off:1; + uint64_t len_m8:1; + uint64_t reset:1; + uint64_t addpkt:1; + uint64_t naddbuf:1; + uint64_t pkt_lend:1; + uint64_t wqe_lend:1; + uint64_t pbp_en:1; + uint64_t opc_mode:2; + uint64_t ipd_en:1; + } cn58xx; + struct cvmx_ipd_ctl_status_cn63xxp1 { + uint64_t reserved_16_63:48; + uint64_t clken:1; + uint64_t no_wptr:1; + uint64_t pq_apkt:1; + uint64_t pq_nabuf:1; + uint64_t ipd_full:1; + uint64_t pkt_off:1; + uint64_t len_m8:1; + uint64_t reset:1; + uint64_t addpkt:1; + uint64_t naddbuf:1; + uint64_t pkt_lend:1; + uint64_t wqe_lend:1; + uint64_t pbp_en:1; + uint64_t opc_mode:2; + uint64_t ipd_en:1; + } cn63xxp1; +}; + +union cvmx_ipd_sub_port_fcs { + uint64_t u64; + struct cvmx_ipd_sub_port_fcs_s { + uint64_t port_bit:32; + uint64_t reserved_32_35:4; + uint64_t port_bit2:4; + uint64_t reserved_40_63:24; + } s; + struct cvmx_ipd_sub_port_fcs_cn30xx { + uint64_t port_bit:3; + uint64_t reserved_3_63:61; + } cn30xx; + struct cvmx_ipd_sub_port_fcs_cn38xx { + uint64_t port_bit:32; + uint64_t reserved_32_63:32; + } cn38xx; +}; + +union cvmx_ipd_sub_port_qos_cnt { + uint64_t u64; + struct cvmx_ipd_sub_port_qos_cnt_s { + uint64_t cnt:32; + uint64_t port_qos:9; + uint64_t reserved_41_63:23; + } s; +}; +typedef struct { + uint32_t dropped_octets; + uint32_t dropped_packets; + uint32_t pci_raw_packets; + uint32_t octets; + uint32_t packets; + uint32_t multicast_packets; + uint32_t broadcast_packets; + uint32_t len_64_packets; + uint32_t len_65_127_packets; + uint32_t len_128_255_packets; + uint32_t len_256_511_packets; + uint32_t len_512_1023_packets; + uint32_t len_1024_1518_packets; + uint32_t len_1519_max_packets; + uint32_t fcs_align_err_packets; + uint32_t runt_packets; + uint32_t runt_crc_packets; + uint32_t oversize_packets; + uint32_t oversize_crc_packets; + uint32_t inb_packets; + uint64_t inb_octets; + uint16_t inb_errors; +} cvmx_pip_port_status_t; + +typedef struct { + uint32_t packets; + uint64_t octets; + uint64_t doorbell; +} cvmx_pko_port_status_t; + +union cvmx_pip_frm_len_chkx { + uint64_t u64; + struct cvmx_pip_frm_len_chkx_s { + uint64_t reserved_32_63:32; + uint64_t maxlen:16; + uint64_t minlen:16; + } s; +}; + +union cvmx_gmxx_rxx_frm_ctl { + uint64_t u64; + struct cvmx_gmxx_rxx_frm_ctl_s { + uint64_t pre_chk:1; + uint64_t pre_strp:1; + uint64_t ctl_drp:1; + uint64_t ctl_bck:1; + uint64_t ctl_mcst:1; + uint64_t ctl_smac:1; + uint64_t pre_free:1; + uint64_t vlan_len:1; + uint64_t pad_len:1; + uint64_t pre_align:1; + uint64_t null_dis:1; + uint64_t reserved_11_11:1; + uint64_t ptp_mode:1; + uint64_t reserved_13_63:51; + } s; + struct cvmx_gmxx_rxx_frm_ctl_cn30xx { + uint64_t pre_chk:1; + uint64_t pre_strp:1; + uint64_t ctl_drp:1; + uint64_t ctl_bck:1; + uint64_t ctl_mcst:1; + uint64_t ctl_smac:1; + uint64_t pre_free:1; + uint64_t vlan_len:1; + uint64_t pad_len:1; + uint64_t reserved_9_63:55; + } cn30xx; + struct cvmx_gmxx_rxx_frm_ctl_cn31xx { + uint64_t pre_chk:1; + uint64_t pre_strp:1; + uint64_t ctl_drp:1; + uint64_t ctl_bck:1; + uint64_t ctl_mcst:1; + uint64_t ctl_smac:1; + uint64_t pre_free:1; + uint64_t vlan_len:1; + uint64_t reserved_8_63:56; + } cn31xx; + struct cvmx_gmxx_rxx_frm_ctl_cn50xx { + uint64_t pre_chk:1; + uint64_t pre_strp:1; + uint64_t ctl_drp:1; + uint64_t ctl_bck:1; + uint64_t ctl_mcst:1; + uint64_t ctl_smac:1; + uint64_t pre_free:1; + uint64_t reserved_7_8:2; + uint64_t pre_align:1; + uint64_t null_dis:1; + uint64_t reserved_11_63:53; + } cn50xx; + struct cvmx_gmxx_rxx_frm_ctl_cn56xxp1 { + uint64_t pre_chk:1; + uint64_t pre_strp:1; + uint64_t ctl_drp:1; + uint64_t ctl_bck:1; + uint64_t ctl_mcst:1; + uint64_t ctl_smac:1; + uint64_t pre_free:1; + uint64_t reserved_7_8:2; + uint64_t pre_align:1; + uint64_t reserved_10_63:54; + } cn56xxp1; + struct cvmx_gmxx_rxx_frm_ctl_cn58xx { + uint64_t pre_chk:1; + uint64_t pre_strp:1; + uint64_t ctl_drp:1; + uint64_t ctl_bck:1; + uint64_t ctl_mcst:1; + uint64_t ctl_smac:1; + uint64_t pre_free:1; + uint64_t vlan_len:1; + uint64_t pad_len:1; + uint64_t pre_align:1; + uint64_t null_dis:1; + uint64_t reserved_11_63:53; + } cn58xx; + struct cvmx_gmxx_rxx_frm_ctl_cn61xx { + uint64_t pre_chk:1; + uint64_t pre_strp:1; + uint64_t ctl_drp:1; + uint64_t ctl_bck:1; + uint64_t ctl_mcst:1; + uint64_t ctl_smac:1; + uint64_t pre_free:1; + uint64_t reserved_7_8:2; + uint64_t pre_align:1; + uint64_t null_dis:1; + uint64_t reserved_11_11:1; + uint64_t ptp_mode:1; + uint64_t reserved_13_63:51; + } cn61xx; +}; + +union cvmx_gmxx_rxx_int_reg { + uint64_t u64; + struct cvmx_gmxx_rxx_int_reg_s { + uint64_t minerr:1; + uint64_t carext:1; + uint64_t maxerr:1; + uint64_t jabber:1; + uint64_t fcserr:1; + uint64_t alnerr:1; + uint64_t lenerr:1; + uint64_t rcverr:1; + uint64_t skperr:1; + uint64_t niberr:1; + uint64_t ovrerr:1; + uint64_t pcterr:1; + uint64_t rsverr:1; + uint64_t falerr:1; + uint64_t coldet:1; + uint64_t ifgerr:1; + uint64_t phy_link:1; + uint64_t phy_spd:1; + uint64_t phy_dupx:1; + uint64_t pause_drp:1; + uint64_t loc_fault:1; + uint64_t rem_fault:1; + uint64_t bad_seq:1; + uint64_t bad_term:1; + uint64_t unsop:1; + uint64_t uneop:1; + uint64_t undat:1; + uint64_t hg2fld:1; + uint64_t hg2cc:1; + uint64_t reserved_29_63:35; + } s; + struct cvmx_gmxx_rxx_int_reg_cn30xx { + uint64_t minerr:1; + uint64_t carext:1; + uint64_t maxerr:1; + uint64_t jabber:1; + uint64_t fcserr:1; + uint64_t alnerr:1; + uint64_t lenerr:1; + uint64_t rcverr:1; + uint64_t skperr:1; + uint64_t niberr:1; + uint64_t ovrerr:1; + uint64_t pcterr:1; + uint64_t rsverr:1; + uint64_t falerr:1; + uint64_t coldet:1; + uint64_t ifgerr:1; + uint64_t phy_link:1; + uint64_t phy_spd:1; + uint64_t phy_dupx:1; + uint64_t reserved_19_63:45; + } cn30xx; + struct cvmx_gmxx_rxx_int_reg_cn50xx { + uint64_t reserved_0_0:1; + uint64_t carext:1; + uint64_t reserved_2_2:1; + uint64_t jabber:1; + uint64_t fcserr:1; + uint64_t alnerr:1; + uint64_t reserved_6_6:1; + uint64_t rcverr:1; + uint64_t skperr:1; + uint64_t niberr:1; + uint64_t ovrerr:1; + uint64_t pcterr:1; + uint64_t rsverr:1; + uint64_t falerr:1; + uint64_t coldet:1; + uint64_t ifgerr:1; + uint64_t phy_link:1; + uint64_t phy_spd:1; + uint64_t phy_dupx:1; + uint64_t pause_drp:1; + uint64_t reserved_20_63:44; + } cn50xx; + struct cvmx_gmxx_rxx_int_reg_cn52xx { + uint64_t reserved_0_0:1; + uint64_t carext:1; + uint64_t reserved_2_2:1; + uint64_t jabber:1; + uint64_t fcserr:1; + uint64_t reserved_5_6:2; + uint64_t rcverr:1; + uint64_t skperr:1; + uint64_t reserved_9_9:1; + uint64_t ovrerr:1; + uint64_t pcterr:1; + uint64_t rsverr:1; + uint64_t falerr:1; + uint64_t coldet:1; + uint64_t ifgerr:1; + uint64_t reserved_16_18:3; + uint64_t pause_drp:1; + uint64_t loc_fault:1; + uint64_t rem_fault:1; + uint64_t bad_seq:1; + uint64_t bad_term:1; + uint64_t unsop:1; + uint64_t uneop:1; + uint64_t undat:1; + uint64_t hg2fld:1; + uint64_t hg2cc:1; + uint64_t reserved_29_63:35; + } cn52xx; + struct cvmx_gmxx_rxx_int_reg_cn56xxp1 { + uint64_t reserved_0_0:1; + uint64_t carext:1; + uint64_t reserved_2_2:1; + uint64_t jabber:1; + uint64_t fcserr:1; + uint64_t reserved_5_6:2; + uint64_t rcverr:1; + uint64_t skperr:1; + uint64_t reserved_9_9:1; + uint64_t ovrerr:1; + uint64_t pcterr:1; + uint64_t rsverr:1; + uint64_t falerr:1; + uint64_t coldet:1; + uint64_t ifgerr:1; + uint64_t reserved_16_18:3; + uint64_t pause_drp:1; + uint64_t loc_fault:1; + uint64_t rem_fault:1; + uint64_t bad_seq:1; + uint64_t bad_term:1; + uint64_t unsop:1; + uint64_t uneop:1; + uint64_t undat:1; + uint64_t reserved_27_63:37; + } cn56xxp1; + struct cvmx_gmxx_rxx_int_reg_cn58xx { + uint64_t minerr:1; + uint64_t carext:1; + uint64_t maxerr:1; + uint64_t jabber:1; + uint64_t fcserr:1; + uint64_t alnerr:1; + uint64_t lenerr:1; + uint64_t rcverr:1; + uint64_t skperr:1; + uint64_t niberr:1; + uint64_t ovrerr:1; + uint64_t pcterr:1; + uint64_t rsverr:1; + uint64_t falerr:1; + uint64_t coldet:1; + uint64_t ifgerr:1; + uint64_t phy_link:1; + uint64_t phy_spd:1; + uint64_t phy_dupx:1; + uint64_t pause_drp:1; + uint64_t reserved_20_63:44; + } cn58xx; + struct cvmx_gmxx_rxx_int_reg_cn61xx { + uint64_t minerr:1; + uint64_t carext:1; + uint64_t reserved_2_2:1; + uint64_t jabber:1; + uint64_t fcserr:1; + uint64_t reserved_5_6:2; + uint64_t rcverr:1; + uint64_t skperr:1; + uint64_t reserved_9_9:1; + uint64_t ovrerr:1; + uint64_t pcterr:1; + uint64_t rsverr:1; + uint64_t falerr:1; + uint64_t coldet:1; + uint64_t ifgerr:1; + uint64_t reserved_16_18:3; + uint64_t pause_drp:1; + uint64_t loc_fault:1; + uint64_t rem_fault:1; + uint64_t bad_seq:1; + uint64_t bad_term:1; + uint64_t unsop:1; + uint64_t uneop:1; + uint64_t undat:1; + uint64_t hg2fld:1; + uint64_t hg2cc:1; + uint64_t reserved_29_63:35; + } cn61xx; +}; + +union cvmx_gmxx_prtx_cfg { + uint64_t u64; + struct cvmx_gmxx_prtx_cfg_s { + uint64_t reserved_22_63:42; + uint64_t pknd:6; + uint64_t reserved_14_15:2; + uint64_t tx_idle:1; + uint64_t rx_idle:1; + uint64_t reserved_9_11:3; + uint64_t speed_msb:1; + uint64_t reserved_4_7:4; + uint64_t slottime:1; + uint64_t duplex:1; + uint64_t speed:1; + uint64_t en:1; + } s; + struct cvmx_gmxx_prtx_cfg_cn30xx { + uint64_t reserved_4_63:60; + uint64_t slottime:1; + uint64_t duplex:1; + uint64_t speed:1; + uint64_t en:1; + } cn30xx; + struct cvmx_gmxx_prtx_cfg_cn52xx { + uint64_t reserved_14_63:50; + uint64_t tx_idle:1; + uint64_t rx_idle:1; + uint64_t reserved_9_11:3; + uint64_t speed_msb:1; + uint64_t reserved_4_7:4; + uint64_t slottime:1; + uint64_t duplex:1; + uint64_t speed:1; + uint64_t en:1; + } cn52xx; +}; + +union cvmx_gmxx_rxx_adr_ctl { + uint64_t u64; + struct cvmx_gmxx_rxx_adr_ctl_s { + uint64_t reserved_4_63:60; + uint64_t cam_mode:1; + uint64_t mcst:2; + uint64_t bcst:1; + } s; +}; + +union cvmx_pip_prt_tagx { + uint64_t u64; + struct cvmx_pip_prt_tagx_s { + uint64_t reserved_54_63:10; + uint64_t portadd_en:1; + uint64_t inc_hwchk:1; + uint64_t reserved_50_51:2; + uint64_t grptagbase_msb:2; + uint64_t reserved_46_47:2; + uint64_t grptagmask_msb:2; + uint64_t reserved_42_43:2; + uint64_t grp_msb:2; + uint64_t grptagbase:4; + uint64_t grptagmask:4; + uint64_t grptag:1; + uint64_t grptag_mskip:1; + uint64_t tag_mode:2; + uint64_t inc_vs:2; + uint64_t inc_vlan:1; + uint64_t inc_prt_flag:1; + uint64_t ip6_dprt_flag:1; + uint64_t ip4_dprt_flag:1; + uint64_t ip6_sprt_flag:1; + uint64_t ip4_sprt_flag:1; + uint64_t ip6_nxth_flag:1; + uint64_t ip4_pctl_flag:1; + uint64_t ip6_dst_flag:1; + uint64_t ip4_dst_flag:1; + uint64_t ip6_src_flag:1; + uint64_t ip4_src_flag:1; + uint64_t tcp6_tag_type:2; + uint64_t tcp4_tag_type:2; + uint64_t ip6_tag_type:2; + uint64_t ip4_tag_type:2; + uint64_t non_tag_type:2; + uint64_t grp:4; + } s; + struct cvmx_pip_prt_tagx_cn30xx { + uint64_t reserved_40_63:24; + uint64_t grptagbase:4; + uint64_t grptagmask:4; + uint64_t grptag:1; + uint64_t reserved_30_30:1; + uint64_t tag_mode:2; + uint64_t inc_vs:2; + uint64_t inc_vlan:1; + uint64_t inc_prt_flag:1; + uint64_t ip6_dprt_flag:1; + uint64_t ip4_dprt_flag:1; + uint64_t ip6_sprt_flag:1; + uint64_t ip4_sprt_flag:1; + uint64_t ip6_nxth_flag:1; + uint64_t ip4_pctl_flag:1; + uint64_t ip6_dst_flag:1; + uint64_t ip4_dst_flag:1; + uint64_t ip6_src_flag:1; + uint64_t ip4_src_flag:1; + uint64_t tcp6_tag_type:2; + uint64_t tcp4_tag_type:2; + uint64_t ip6_tag_type:2; + uint64_t ip4_tag_type:2; + uint64_t non_tag_type:2; + uint64_t grp:4; + } cn30xx; + struct cvmx_pip_prt_tagx_cn50xx { + uint64_t reserved_40_63:24; + uint64_t grptagbase:4; + uint64_t grptagmask:4; + uint64_t grptag:1; + uint64_t grptag_mskip:1; + uint64_t tag_mode:2; + uint64_t inc_vs:2; + uint64_t inc_vlan:1; + uint64_t inc_prt_flag:1; + uint64_t ip6_dprt_flag:1; + uint64_t ip4_dprt_flag:1; + uint64_t ip6_sprt_flag:1; + uint64_t ip4_sprt_flag:1; + uint64_t ip6_nxth_flag:1; + uint64_t ip4_pctl_flag:1; + uint64_t ip6_dst_flag:1; + uint64_t ip4_dst_flag:1; + uint64_t ip6_src_flag:1; + uint64_t ip4_src_flag:1; + uint64_t tcp6_tag_type:2; + uint64_t tcp4_tag_type:2; + uint64_t ip6_tag_type:2; + uint64_t ip4_tag_type:2; + uint64_t non_tag_type:2; + uint64_t grp:4; + } cn50xx; +}; + +union cvmx_spxx_int_reg { + uint64_t u64; + struct cvmx_spxx_int_reg_s { + uint64_t reserved_32_63:32; + uint64_t spf:1; + uint64_t reserved_12_30:19; + uint64_t calerr:1; + uint64_t syncerr:1; + uint64_t diperr:1; + uint64_t tpaovr:1; + uint64_t rsverr:1; + uint64_t drwnng:1; + uint64_t clserr:1; + uint64_t spiovr:1; + uint64_t reserved_2_3:2; + uint64_t abnorm:1; + uint64_t prtnxa:1; + } s; +}; + +union cvmx_spxx_int_msk { + uint64_t u64; + struct cvmx_spxx_int_msk_s { + uint64_t reserved_12_63:52; + uint64_t calerr:1; + uint64_t syncerr:1; + uint64_t diperr:1; + uint64_t tpaovr:1; + uint64_t rsverr:1; + uint64_t drwnng:1; + uint64_t clserr:1; + uint64_t spiovr:1; + uint64_t reserved_2_3:2; + uint64_t abnorm:1; + uint64_t prtnxa:1; + } s; +}; + +union cvmx_pow_wq_int { + uint64_t u64; + struct cvmx_pow_wq_int_s { + uint64_t wq_int:16; + uint64_t iq_dis:16; + uint64_t reserved_32_63:32; + } s; +}; + +union cvmx_sso_wq_int_thrx { + uint64_t u64; + struct { + uint64_t iq_thr:12; + uint64_t reserved_12_13:2; + uint64_t ds_thr:12; + uint64_t reserved_26_27:2; + uint64_t tc_thr:4; + uint64_t tc_en:1; + uint64_t reserved_33_63:31; + } s; +}; + +union cvmx_stxx_int_reg { + uint64_t u64; + struct cvmx_stxx_int_reg_s { + uint64_t reserved_9_63:55; + uint64_t syncerr:1; + uint64_t frmerr:1; + uint64_t unxfrm:1; + uint64_t nosync:1; + uint64_t diperr:1; + uint64_t datovr:1; + uint64_t ovrbst:1; + uint64_t calpar1:1; + uint64_t calpar0:1; + } s; +}; + +union cvmx_stxx_int_msk { + uint64_t u64; + struct cvmx_stxx_int_msk_s { + uint64_t reserved_8_63:56; + uint64_t frmerr:1; + uint64_t unxfrm:1; + uint64_t nosync:1; + uint64_t diperr:1; + uint64_t datovr:1; + uint64_t ovrbst:1; + uint64_t calpar1:1; + uint64_t calpar0:1; + } s; +}; + +union cvmx_pow_wq_int_pc { + uint64_t u64; + struct cvmx_pow_wq_int_pc_s { + uint64_t reserved_0_7:8; + uint64_t pc_thr:20; + uint64_t reserved_28_31:4; + uint64_t pc:28; + uint64_t reserved_60_63:4; + } s; +}; + +union cvmx_pow_wq_int_thrx { + uint64_t u64; + struct cvmx_pow_wq_int_thrx_s { + uint64_t reserved_29_63:35; + uint64_t tc_en:1; + uint64_t tc_thr:4; + uint64_t reserved_23_23:1; + uint64_t ds_thr:11; + uint64_t reserved_11_11:1; + uint64_t iq_thr:11; + } s; + struct cvmx_pow_wq_int_thrx_cn30xx { + uint64_t reserved_29_63:35; + uint64_t tc_en:1; + uint64_t tc_thr:4; + uint64_t reserved_18_23:6; + uint64_t ds_thr:6; + uint64_t reserved_6_11:6; + uint64_t iq_thr:6; + } cn30xx; + struct cvmx_pow_wq_int_thrx_cn31xx { + uint64_t reserved_29_63:35; + uint64_t tc_en:1; + uint64_t tc_thr:4; + uint64_t reserved_20_23:4; + uint64_t ds_thr:8; + uint64_t reserved_8_11:4; + uint64_t iq_thr:8; + } cn31xx; + struct cvmx_pow_wq_int_thrx_cn52xx { + uint64_t reserved_29_63:35; + uint64_t tc_en:1; + uint64_t tc_thr:4; + uint64_t reserved_21_23:3; + uint64_t ds_thr:9; + uint64_t reserved_9_11:3; + uint64_t iq_thr:9; + } cn52xx; + struct cvmx_pow_wq_int_thrx_cn63xx { + uint64_t reserved_29_63:35; + uint64_t tc_en:1; + uint64_t tc_thr:4; + uint64_t reserved_22_23:2; + uint64_t ds_thr:10; + uint64_t reserved_10_11:2; + uint64_t iq_thr:10; + } cn63xx; +}; + +union cvmx_npi_rsl_int_blocks { + uint64_t u64; + struct cvmx_npi_rsl_int_blocks_s { + uint64_t reserved_32_63:32; + uint64_t rint_31:1; + uint64_t iob:1; + uint64_t reserved_28_29:2; + uint64_t rint_27:1; + uint64_t rint_26:1; + uint64_t rint_25:1; + uint64_t rint_24:1; + uint64_t asx1:1; + uint64_t asx0:1; + uint64_t rint_21:1; + uint64_t pip:1; + uint64_t spx1:1; + uint64_t spx0:1; + uint64_t lmc:1; + uint64_t l2c:1; + uint64_t rint_15:1; + uint64_t reserved_13_14:2; + uint64_t pow:1; + uint64_t tim:1; + uint64_t pko:1; + uint64_t ipd:1; + uint64_t rint_8:1; + uint64_t zip:1; + uint64_t dfa:1; + uint64_t fpa:1; + uint64_t key:1; + uint64_t npi:1; + uint64_t gmx1:1; + uint64_t gmx0:1; + uint64_t mio:1; + } s; + struct cvmx_npi_rsl_int_blocks_cn30xx { + uint64_t reserved_32_63:32; + uint64_t rint_31:1; + uint64_t iob:1; + uint64_t rint_29:1; + uint64_t rint_28:1; + uint64_t rint_27:1; + uint64_t rint_26:1; + uint64_t rint_25:1; + uint64_t rint_24:1; + uint64_t asx1:1; + uint64_t asx0:1; + uint64_t rint_21:1; + uint64_t pip:1; + uint64_t spx1:1; + uint64_t spx0:1; + uint64_t lmc:1; + uint64_t l2c:1; + uint64_t rint_15:1; + uint64_t rint_14:1; + uint64_t usb:1; + uint64_t pow:1; + uint64_t tim:1; + uint64_t pko:1; + uint64_t ipd:1; + uint64_t rint_8:1; + uint64_t zip:1; + uint64_t dfa:1; + uint64_t fpa:1; + uint64_t key:1; + uint64_t npi:1; + uint64_t gmx1:1; + uint64_t gmx0:1; + uint64_t mio:1; + } cn30xx; + struct cvmx_npi_rsl_int_blocks_cn38xx { + uint64_t reserved_32_63:32; + uint64_t rint_31:1; + uint64_t iob:1; + uint64_t rint_29:1; + uint64_t rint_28:1; + uint64_t rint_27:1; + uint64_t rint_26:1; + uint64_t rint_25:1; + uint64_t rint_24:1; + uint64_t asx1:1; + uint64_t asx0:1; + uint64_t rint_21:1; + uint64_t pip:1; + uint64_t spx1:1; + uint64_t spx0:1; + uint64_t lmc:1; + uint64_t l2c:1; + uint64_t rint_15:1; + uint64_t rint_14:1; + uint64_t rint_13:1; + uint64_t pow:1; + uint64_t tim:1; + uint64_t pko:1; + uint64_t ipd:1; + uint64_t rint_8:1; + uint64_t zip:1; + uint64_t dfa:1; + uint64_t fpa:1; + uint64_t key:1; + uint64_t npi:1; + uint64_t gmx1:1; + uint64_t gmx0:1; + uint64_t mio:1; + } cn38xx; + struct cvmx_npi_rsl_int_blocks_cn50xx { + uint64_t reserved_31_63:33; + uint64_t iob:1; + uint64_t lmc1:1; + uint64_t agl:1; + uint64_t reserved_24_27:4; + uint64_t asx1:1; + uint64_t asx0:1; + uint64_t reserved_21_21:1; + uint64_t pip:1; + uint64_t spx1:1; + uint64_t spx0:1; + uint64_t lmc:1; + uint64_t l2c:1; + uint64_t reserved_15_15:1; + uint64_t rad:1; + uint64_t usb:1; + uint64_t pow:1; + uint64_t tim:1; + uint64_t pko:1; + uint64_t ipd:1; + uint64_t reserved_8_8:1; + uint64_t zip:1; + uint64_t dfa:1; + uint64_t fpa:1; + uint64_t key:1; + uint64_t npi:1; + uint64_t gmx1:1; + uint64_t gmx0:1; + uint64_t mio:1; + } cn50xx; +}; + +union cvmx_pko_command_word0 { + uint64_t u64; + struct { + uint64_t total_bytes:16; + uint64_t segs:6; + uint64_t dontfree:1; + uint64_t ignore_i:1; + uint64_t ipoffp1:7; + uint64_t gather:1; + uint64_t rsp:1; + uint64_t wqp:1; + uint64_t n2:1; + uint64_t le:1; + uint64_t reg0:11; + uint64_t subone0:1; + uint64_t reg1:11; + uint64_t subone1:1; + uint64_t size0:2; + uint64_t size1:2; + } s; +}; + +union cvmx_ciu_timx { + uint64_t u64; + struct cvmx_ciu_timx_s { + uint64_t reserved_37_63:27; + uint64_t one_shot:1; + uint64_t len:36; + } s; +}; + +union cvmx_gmxx_rxx_rx_inbnd { + uint64_t u64; + struct cvmx_gmxx_rxx_rx_inbnd_s { + uint64_t status:1; + uint64_t speed:2; + uint64_t duplex:1; + uint64_t reserved_4_63:60; + } s; +}; + +static inline int32_t cvmx_fau_fetch_and_add32(enum cvmx_fau_reg_32 reg, + int32_t value) +{ + return value; +} + +static inline void cvmx_fau_atomic_add32(enum cvmx_fau_reg_32 reg, + int32_t value) +{ } + +static inline void cvmx_fau_atomic_write32(enum cvmx_fau_reg_32 reg, + int32_t value) +{ } + +static inline uint64_t cvmx_scratch_read64(uint64_t address) +{ + return 0; +} + +static inline void cvmx_scratch_write64(uint64_t address, uint64_t value) +{ } + +static inline int cvmx_wqe_get_grp(struct cvmx_wqe *work) +{ + return 0; +} + +static inline void *cvmx_phys_to_ptr(uint64_t physical_address) +{ + return (void *)(uintptr_t)(physical_address); +} + +static inline uint64_t cvmx_ptr_to_phys(void *ptr) +{ + return (unsigned long)ptr; +} + +static inline int cvmx_helper_get_interface_num(int ipd_port) +{ + return ipd_port; +} + +static inline int cvmx_helper_get_interface_index_num(int ipd_port) +{ + return ipd_port; +} + +static inline void cvmx_fpa_enable(void) +{ } + +static inline uint64_t cvmx_read_csr(uint64_t csr_addr) +{ + return 0; +} + +static inline void cvmx_write_csr(uint64_t csr_addr, uint64_t val) +{ } + +static inline int cvmx_helper_setup_red(int pass_thresh, int drop_thresh) +{ + return 0; +} + +static inline void *cvmx_fpa_alloc(uint64_t pool) +{ + return NULL; +} + +static inline void cvmx_fpa_free(void *ptr, uint64_t pool, + uint64_t num_cache_lines) +{ } + +static inline int octeon_is_simulation(void) +{ + return 1; +} + +static inline void cvmx_pip_get_port_status(uint64_t port_num, uint64_t clear, + cvmx_pip_port_status_t *status) +{ } + +static inline void cvmx_pko_get_port_status(uint64_t port_num, uint64_t clear, + cvmx_pko_port_status_t *status) +{ } + +static inline cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int + interface) +{ + return 0; +} + +static inline union cvmx_helper_link_info cvmx_helper_link_get(int ipd_port) +{ + union cvmx_helper_link_info ret = { .u64 = 0 }; + + return ret; +} + +static inline int cvmx_helper_link_set(int ipd_port, + union cvmx_helper_link_info link_info) +{ + return 0; +} + +static inline int cvmx_helper_initialize_packet_io_global(void) +{ + return 0; +} + +static inline int cvmx_helper_get_number_of_interfaces(void) +{ + return 2; +} + +static inline int cvmx_helper_ports_on_interface(int interface) +{ + return 1; +} + +static inline int cvmx_helper_get_ipd_port(int interface, int port) +{ + return 0; +} + +static inline int cvmx_helper_ipd_and_packet_input_enable(void) +{ + return 0; +} + +static inline void cvmx_ipd_disable(void) +{ } + +static inline void cvmx_ipd_free_ptr(void) +{ } + +static inline void cvmx_pko_disable(void) +{ } + +static inline void cvmx_pko_shutdown(void) +{ } + +static inline int cvmx_pko_get_base_queue_per_core(int port, int core) +{ + return port; +} + +static inline int cvmx_pko_get_base_queue(int port) +{ + return port; +} + +static inline int cvmx_pko_get_num_queues(int port) +{ + return port; +} + +static inline unsigned int cvmx_get_core_num(void) +{ + return 0; +} + +static inline void cvmx_pow_work_request_async_nocheck(int scr_addr, + cvmx_pow_wait_t wait) +{ } + +static inline void cvmx_pow_work_request_async(int scr_addr, + cvmx_pow_wait_t wait) +{ } + +static inline struct cvmx_wqe *cvmx_pow_work_response_async(int scr_addr) +{ + struct cvmx_wqe *wqe = (void *)(unsigned long)scr_addr; + + return wqe; +} + +static inline struct cvmx_wqe *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) +{ + return (void *)(unsigned long)wait; +} + +static inline int cvmx_spi_restart_interface(int interface, + cvmx_spi_mode_t mode, int timeout) +{ + return 0; +} + +static inline void cvmx_fau_async_fetch_and_add32(uint64_t scraddr, + enum cvmx_fau_reg_32 reg, + int32_t value) +{ } + +static inline union cvmx_gmxx_rxx_rx_inbnd cvmx_spi4000_check_speed( + int interface, + int port) +{ + union cvmx_gmxx_rxx_rx_inbnd r; + + r.u64 = 0; + return r; +} + +static inline void cvmx_pko_send_packet_prepare(uint64_t port, uint64_t queue, + cvmx_pko_lock_t use_locking) +{ } + +static inline cvmx_pko_status_t cvmx_pko_send_packet_finish(uint64_t port, + uint64_t queue, union cvmx_pko_command_word0 pko_command, + union cvmx_buf_ptr packet, cvmx_pko_lock_t use_locking) +{ + return 0; +} + +static inline void cvmx_wqe_set_port(struct cvmx_wqe *work, int port) +{ } + +static inline void cvmx_wqe_set_qos(struct cvmx_wqe *work, int qos) +{ } + +static inline int cvmx_wqe_get_qos(struct cvmx_wqe *work) +{ + return 0; +} + +static inline void cvmx_wqe_set_grp(struct cvmx_wqe *work, int grp) +{ } + +static inline void cvmx_pow_work_submit(struct cvmx_wqe *wqp, uint32_t tag, + enum cvmx_pow_tag_type tag_type, + uint64_t qos, uint64_t grp) +{ } + +#define CVMX_ASXX_RX_CLK_SETX(a, b) ((a)+(b)) +#define CVMX_ASXX_TX_CLK_SETX(a, b) ((a)+(b)) +#define CVMX_CIU_TIMX(a) (a) +#define CVMX_GMXX_RXX_ADR_CAM0(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_ADR_CAM1(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_ADR_CAM2(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_ADR_CAM3(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_ADR_CAM4(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_ADR_CAM5(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_FRM_CTL(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_INT_REG(a, b) ((a)+(b)) +#define CVMX_GMXX_SMACX(a, b) ((a)+(b)) +#define CVMX_PIP_PRT_TAGX(a) (a) +#define CVMX_POW_PP_GRP_MSKX(a) (a) +#define CVMX_POW_WQ_INT_THRX(a) (a) +#define CVMX_SPXX_INT_MSK(a) (a) +#define CVMX_SPXX_INT_REG(a) (a) +#define CVMX_SSO_PPX_GRP_MSK(a) (a) +#define CVMX_SSO_WQ_INT_THRX(a) (a) +#define CVMX_STXX_INT_MSK(a) (a) +#define CVMX_STXX_INT_REG(a) (a) From f9546de43df2b1f9606e9baa9f3f0399c4d1ca0b Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 5 Feb 2020 13:11:13 +1300 Subject: [PATCH 0156/2231] MIPS: octeon: remove typedef declaration for cvmx_wqe Remove typedef declaration from struct cvmx_wqe and replace its previous uses with new struct declaration. Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20200205001116.14096-4-chris.packham@alliedtelesis.co.nz Signed-off-by: Greg Kroah-Hartman --- .../cavium-octeon/executive/cvmx-helper.c | 2 +- .../include/asm/octeon/cvmx-helper-util.h | 2 +- arch/mips/include/asm/octeon/cvmx-pko.h | 2 +- arch/mips/include/asm/octeon/cvmx-pow.h | 22 +++++++++---------- arch/mips/include/asm/octeon/cvmx-wqe.h | 16 +++++++------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper.c b/arch/mips/cavium-octeon/executive/cvmx-helper.c index de391541d6f7..bb84ae33399a 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c @@ -784,7 +784,7 @@ static int __cvmx_helper_errata_fix_ipd_ptr_alignment(void) uint64_t *p64; cvmx_pko_command_word0_t pko_command; union cvmx_buf_ptr g_buffer, pkt_buffer; - cvmx_wqe_t *work; + struct cvmx_wqe *work; int size, num_segs = 0, wqe_pcnt, pkt_pcnt; union cvmx_gmxx_prtx_cfg gmx_cfg; int retry_cnt; diff --git a/arch/mips/include/asm/octeon/cvmx-helper-util.h b/arch/mips/include/asm/octeon/cvmx-helper-util.h index e9a97e7ee604..97b27a07cfb0 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper-util.h +++ b/arch/mips/include/asm/octeon/cvmx-helper-util.h @@ -123,7 +123,7 @@ static inline int cvmx_helper_get_last_ipd_port(int interface) * * @work: Work queue entry with packet to free */ -static inline void cvmx_helper_free_packet_data(cvmx_wqe_t *work) +static inline void cvmx_helper_free_packet_data(struct cvmx_wqe *work) { uint64_t number_buffers; union cvmx_buf_ptr buffer_ptr; diff --git a/arch/mips/include/asm/octeon/cvmx-pko.h b/arch/mips/include/asm/octeon/cvmx-pko.h index 20eb9c46a75a..f033523c19f8 100644 --- a/arch/mips/include/asm/octeon/cvmx-pko.h +++ b/arch/mips/include/asm/octeon/cvmx-pko.h @@ -394,7 +394,7 @@ static inline void cvmx_pko_send_packet_prepare(uint64_t port, uint64_t queue, CVMX_TAG_SW_BITS_INTERNAL << CVMX_TAG_SW_SHIFT | CVMX_TAG_SUBGROUP_PKO << CVMX_TAG_SUBGROUP_SHIFT | (CVMX_TAG_SUBGROUP_MASK & queue); - cvmx_pow_tag_sw_full((cvmx_wqe_t *) cvmx_phys_to_ptr(0x80), tag, + cvmx_pow_tag_sw_full((struct cvmx_wqe *) cvmx_phys_to_ptr(0x80), tag, CVMX_POW_TAG_TYPE_ATOMIC, 0); } } diff --git a/arch/mips/include/asm/octeon/cvmx-pow.h b/arch/mips/include/asm/octeon/cvmx-pow.h index 410bb70e5aac..ba366f4f3c3d 100644 --- a/arch/mips/include/asm/octeon/cvmx-pow.h +++ b/arch/mips/include/asm/octeon/cvmx-pow.h @@ -1283,7 +1283,7 @@ static inline cvmx_pow_tag_req_t cvmx_pow_get_current_tag(void) * * Returns WQE pointer */ -static inline cvmx_wqe_t *cvmx_pow_get_current_wqp(void) +static inline struct cvmx_wqe *cvmx_pow_get_current_wqp(void) { cvmx_pow_load_addr_t load_addr; cvmx_pow_tag_load_resp_t load_resp; @@ -1296,7 +1296,7 @@ static inline cvmx_wqe_t *cvmx_pow_get_current_wqp(void) load_addr.sstatus.get_cur = 1; load_addr.sstatus.get_wqp = 1; load_resp.u64 = cvmx_read_csr(load_addr.u64); - return (cvmx_wqe_t *) cvmx_phys_to_ptr(load_resp.s_sstatus4.wqp); + return (struct cvmx_wqe *) cvmx_phys_to_ptr(load_resp.s_sstatus4.wqp); } #ifndef CVMX_MF_CHORD @@ -1348,7 +1348,7 @@ static inline void cvmx_pow_tag_sw_wait(void) * Returns Returns the WQE pointer from POW. Returns NULL if no work * was available. */ -static inline cvmx_wqe_t *cvmx_pow_work_request_sync_nocheck(cvmx_pow_wait_t +static inline struct cvmx_wqe *cvmx_pow_work_request_sync_nocheck(cvmx_pow_wait_t wait) { cvmx_pow_load_addr_t ptr; @@ -1368,7 +1368,7 @@ static inline cvmx_wqe_t *cvmx_pow_work_request_sync_nocheck(cvmx_pow_wait_t if (result.s_work.no_work) return NULL; else - return (cvmx_wqe_t *) cvmx_phys_to_ptr(result.s_work.addr); + return (struct cvmx_wqe *) cvmx_phys_to_ptr(result.s_work.addr); } /** @@ -1382,7 +1382,7 @@ static inline cvmx_wqe_t *cvmx_pow_work_request_sync_nocheck(cvmx_pow_wait_t * Returns Returns the WQE pointer from POW. Returns NULL if no work * was available. */ -static inline cvmx_wqe_t *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) +static inline struct cvmx_wqe *cvmx_pow_work_request_sync(cvmx_pow_wait_t wait) { if (CVMX_ENABLE_POW_CHECKS) __cvmx_pow_warn_if_pending_switch(__func__); @@ -1485,7 +1485,7 @@ static inline void cvmx_pow_work_request_async(int scr_addr, * Returns Returns the WQE from the scratch register, or NULL if no * work was available. */ -static inline cvmx_wqe_t *cvmx_pow_work_response_async(int scr_addr) +static inline struct cvmx_wqe *cvmx_pow_work_response_async(int scr_addr) { cvmx_pow_tag_load_resp_t result; @@ -1495,7 +1495,7 @@ static inline cvmx_wqe_t *cvmx_pow_work_response_async(int scr_addr) if (result.s_work.no_work) return NULL; else - return (cvmx_wqe_t *) cvmx_phys_to_ptr(result.s_work.addr); + return (struct cvmx_wqe *) cvmx_phys_to_ptr(result.s_work.addr); } /** @@ -1508,7 +1508,7 @@ static inline cvmx_wqe_t *cvmx_pow_work_response_async(int scr_addr) * Returns 0 if pointer is valid * 1 if invalid (no work was returned) */ -static inline uint64_t cvmx_pow_work_invalid(cvmx_wqe_t *wqe_ptr) +static inline uint64_t cvmx_pow_work_invalid(struct cvmx_wqe *wqe_ptr) { return wqe_ptr == NULL; } @@ -1638,7 +1638,7 @@ static inline void cvmx_pow_tag_sw(uint32_t tag, * @tag_type: type of tag * @group: group value for the work queue entry. */ -static inline void cvmx_pow_tag_sw_full_nocheck(cvmx_wqe_t *wqp, uint32_t tag, +static inline void cvmx_pow_tag_sw_full_nocheck(struct cvmx_wqe *wqp, uint32_t tag, enum cvmx_pow_tag_type tag_type, uint64_t group) { @@ -1712,7 +1712,7 @@ static inline void cvmx_pow_tag_sw_full_nocheck(cvmx_wqe_t *wqp, uint32_t tag, * @tag_type: type of tag * @group: group value for the work queue entry. */ -static inline void cvmx_pow_tag_sw_full(cvmx_wqe_t *wqp, uint32_t tag, +static inline void cvmx_pow_tag_sw_full(struct cvmx_wqe *wqp, uint32_t tag, enum cvmx_pow_tag_type tag_type, uint64_t group) { @@ -1803,7 +1803,7 @@ static inline void cvmx_pow_tag_sw_null(void) * @qos: Input queue to add to. * @grp: group value for the work queue entry. */ -static inline void cvmx_pow_work_submit(cvmx_wqe_t *wqp, uint32_t tag, +static inline void cvmx_pow_work_submit(struct cvmx_wqe *wqp, uint32_t tag, enum cvmx_pow_tag_type tag_type, uint64_t qos, uint64_t grp) { diff --git a/arch/mips/include/asm/octeon/cvmx-wqe.h b/arch/mips/include/asm/octeon/cvmx-wqe.h index 0d697aa786d4..9cec2299b81b 100644 --- a/arch/mips/include/asm/octeon/cvmx-wqe.h +++ b/arch/mips/include/asm/octeon/cvmx-wqe.h @@ -547,7 +547,7 @@ union cvmx_wqe_word1 { * * must be 8-byte aligned */ -typedef struct { +struct cvmx_wqe { /***************************************************************** * WORD 0 @@ -593,9 +593,9 @@ typedef struct { * */ -} CVMX_CACHE_LINE_ALIGNED cvmx_wqe_t; +} CVMX_CACHE_LINE_ALIGNED; -static inline int cvmx_wqe_get_port(cvmx_wqe_t *work) +static inline int cvmx_wqe_get_port(struct cvmx_wqe *work) { int port; @@ -607,7 +607,7 @@ static inline int cvmx_wqe_get_port(cvmx_wqe_t *work) return port; } -static inline void cvmx_wqe_set_port(cvmx_wqe_t *work, int port) +static inline void cvmx_wqe_set_port(struct cvmx_wqe *work, int port) { if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) work->word2.s_cn68xx.port = port; @@ -615,7 +615,7 @@ static inline void cvmx_wqe_set_port(cvmx_wqe_t *work, int port) work->word1.cn38xx.ipprt = port; } -static inline int cvmx_wqe_get_grp(cvmx_wqe_t *work) +static inline int cvmx_wqe_get_grp(struct cvmx_wqe *work) { int grp; @@ -627,7 +627,7 @@ static inline int cvmx_wqe_get_grp(cvmx_wqe_t *work) return grp; } -static inline void cvmx_wqe_set_grp(cvmx_wqe_t *work, int grp) +static inline void cvmx_wqe_set_grp(struct cvmx_wqe *work, int grp) { if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) work->word1.cn68xx.grp = grp; @@ -635,7 +635,7 @@ static inline void cvmx_wqe_set_grp(cvmx_wqe_t *work, int grp) work->word1.cn38xx.grp = grp; } -static inline int cvmx_wqe_get_qos(cvmx_wqe_t *work) +static inline int cvmx_wqe_get_qos(struct cvmx_wqe *work) { int qos; @@ -647,7 +647,7 @@ static inline int cvmx_wqe_get_qos(cvmx_wqe_t *work) return qos; } -static inline void cvmx_wqe_set_qos(cvmx_wqe_t *work, int qos) +static inline void cvmx_wqe_set_qos(struct cvmx_wqe *work, int qos) { if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE)) work->word1.cn68xx.qos = qos; From f7d2bdcb7ead8ef5cb3253122a0972d1f3bab167 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 5 Feb 2020 13:11:14 +1300 Subject: [PATCH 0157/2231] MIPS: octeon: remove typedef declaration for cvmx_helper_link_info Remove declaration of union cvmx_helper_link_info as typedef and update uses to use the union. Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20200205001116.14096-5-chris.packham@alliedtelesis.co.nz Signed-off-by: Greg Kroah-Hartman --- arch/mips/cavium-octeon/executive/cvmx-helper-board.c | 4 ++-- arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c | 6 +++--- arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c | 8 ++++---- arch/mips/cavium-octeon/executive/cvmx-helper-spi.c | 6 +++--- arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c | 6 +++--- arch/mips/cavium-octeon/executive/cvmx-helper.c | 6 +++--- arch/mips/include/asm/octeon/cvmx-helper-board.h | 2 +- arch/mips/include/asm/octeon/cvmx-helper-rgmii.h | 4 ++-- arch/mips/include/asm/octeon/cvmx-helper-sgmii.h | 4 ++-- arch/mips/include/asm/octeon/cvmx-helper-spi.h | 4 ++-- arch/mips/include/asm/octeon/cvmx-helper-xaui.h | 4 ++-- arch/mips/include/asm/octeon/cvmx-helper.h | 8 ++++---- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c index 2e2d45bc850d..abd11b7af22f 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c @@ -207,9 +207,9 @@ int cvmx_helper_board_get_mii_address(int ipd_port) * Returns The ports link status. If the link isn't fully resolved, this must * return zero. */ -cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port) +union cvmx_helper_link_info __cvmx_helper_board_link_get(int ipd_port) { - cvmx_helper_link_info_t result; + union cvmx_helper_link_info result; WARN(!octeon_is_simulation(), "Using deprecated link status - please update your DT"); diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c index e812ed9a03bb..c4b58598aa9d 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c @@ -261,7 +261,7 @@ int __cvmx_helper_rgmii_enable(int interface) * * Returns Link state */ -cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port) +union cvmx_helper_link_info __cvmx_helper_rgmii_link_get(int ipd_port) { int interface = cvmx_helper_get_interface_num(ipd_port); int index = cvmx_helper_get_interface_index_num(ipd_port); @@ -270,7 +270,7 @@ cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port) asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); if (asxx_prt_loop.s.int_loop & (1 << index)) { /* Force 1Gbps full duplex on internal loopback */ - cvmx_helper_link_info_t result; + union cvmx_helper_link_info result; result.u64 = 0; result.s.full_duplex = 1; result.s.link_up = 1; @@ -292,7 +292,7 @@ cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port) * Returns Zero on success, negative on failure */ int __cvmx_helper_rgmii_link_set(int ipd_port, - cvmx_helper_link_info_t link_info) + union cvmx_helper_link_info link_info) { int result = 0; int interface = cvmx_helper_get_interface_num(ipd_port); diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c index f6ebf63dc84c..e07d8f15efba 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c @@ -200,7 +200,7 @@ static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index) */ static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface, int index, - cvmx_helper_link_info_t + union cvmx_helper_link_info link_info) { int is_enabled; @@ -394,9 +394,9 @@ int __cvmx_helper_sgmii_enable(int interface) * * Returns Link state */ -cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port) +union cvmx_helper_link_info __cvmx_helper_sgmii_link_get(int ipd_port) { - cvmx_helper_link_info_t result; + union cvmx_helper_link_info result; union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg; int interface = cvmx_helper_get_interface_num(ipd_port); int index = cvmx_helper_get_interface_index_num(ipd_port); @@ -505,7 +505,7 @@ cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port) * Returns Zero on success, negative on failure */ int __cvmx_helper_sgmii_link_set(int ipd_port, - cvmx_helper_link_info_t link_info) + union cvmx_helper_link_info link_info) { int interface = cvmx_helper_get_interface_num(ipd_port); int index = cvmx_helper_get_interface_index_num(ipd_port); diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c index 2a574d272671..525914e9b22d 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c @@ -140,9 +140,9 @@ int __cvmx_helper_spi_enable(int interface) * * Returns Link state */ -cvmx_helper_link_info_t __cvmx_helper_spi_link_get(int ipd_port) +union cvmx_helper_link_info __cvmx_helper_spi_link_get(int ipd_port) { - cvmx_helper_link_info_t result; + union cvmx_helper_link_info result; int interface = cvmx_helper_get_interface_num(ipd_port); int index = cvmx_helper_get_interface_index_num(ipd_port); result.u64 = 0; @@ -193,7 +193,7 @@ cvmx_helper_link_info_t __cvmx_helper_spi_link_get(int ipd_port) * * Returns Zero on success, negative on failure */ -int __cvmx_helper_spi_link_set(int ipd_port, cvmx_helper_link_info_t link_info) +int __cvmx_helper_spi_link_set(int ipd_port, union cvmx_helper_link_info link_info) { /* Nothing to do. If we have a SPI4000 then the setup was already performed by cvmx_spi4000_check_speed(). If not then there isn't any link diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c index 93a498d05184..842990e8404f 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c @@ -259,13 +259,13 @@ int __cvmx_helper_xaui_enable(int interface) * * Returns Link state */ -cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port) +union cvmx_helper_link_info __cvmx_helper_xaui_link_get(int ipd_port) { int interface = cvmx_helper_get_interface_num(ipd_port); union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; union cvmx_pcsxx_status1_reg pcsxx_status1_reg; - cvmx_helper_link_info_t result; + union cvmx_helper_link_info result; gmxx_tx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); gmxx_rx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RX_XAUI_CTL(interface)); @@ -299,7 +299,7 @@ cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port) * * Returns Zero on success, negative on failure */ -int __cvmx_helper_xaui_link_set(int ipd_port, cvmx_helper_link_info_t link_info) +int __cvmx_helper_xaui_link_set(int ipd_port, union cvmx_helper_link_info link_info) { int interface = cvmx_helper_get_interface_num(ipd_port); union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper.c b/arch/mips/cavium-octeon/executive/cvmx-helper.c index bb84ae33399a..8002f9d3039d 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c @@ -1075,9 +1075,9 @@ int cvmx_helper_initialize_packet_io_local(void) * * Returns Link state */ -cvmx_helper_link_info_t cvmx_helper_link_get(int ipd_port) +union cvmx_helper_link_info cvmx_helper_link_get(int ipd_port) { - cvmx_helper_link_info_t result; + union cvmx_helper_link_info result; int interface = cvmx_helper_get_interface_num(ipd_port); int index = cvmx_helper_get_interface_index_num(ipd_port); @@ -1136,7 +1136,7 @@ EXPORT_SYMBOL_GPL(cvmx_helper_link_get); * * Returns Zero on success, negative on failure */ -int cvmx_helper_link_set(int ipd_port, cvmx_helper_link_info_t link_info) +int cvmx_helper_link_set(int ipd_port, union cvmx_helper_link_info link_info) { int result = -1; int interface = cvmx_helper_get_interface_num(ipd_port); diff --git a/arch/mips/include/asm/octeon/cvmx-helper-board.h b/arch/mips/include/asm/octeon/cvmx-helper-board.h index d7fdcf0a0088..ce52aafe7a8d 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper-board.h +++ b/arch/mips/include/asm/octeon/cvmx-helper-board.h @@ -93,7 +93,7 @@ extern int cvmx_helper_board_get_mii_address(int ipd_port); * Returns The ports link status. If the link isn't fully resolved, this must * return zero. */ -extern cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port); +extern union cvmx_helper_link_info __cvmx_helper_board_link_get(int ipd_port); /** * This function is called by cvmx_helper_interface_probe() after it diff --git a/arch/mips/include/asm/octeon/cvmx-helper-rgmii.h b/arch/mips/include/asm/octeon/cvmx-helper-rgmii.h index ac42b5066bd9..3e79a7f898b6 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper-rgmii.h +++ b/arch/mips/include/asm/octeon/cvmx-helper-rgmii.h @@ -74,7 +74,7 @@ extern int __cvmx_helper_rgmii_enable(int interface); * * Returns Link state */ -extern cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port); +extern union cvmx_helper_link_info __cvmx_helper_rgmii_link_get(int ipd_port); /** * Configure an IPD/PKO port for the specified link state. This @@ -88,6 +88,6 @@ extern cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port); * Returns Zero on success, negative on failure */ extern int __cvmx_helper_rgmii_link_set(int ipd_port, - cvmx_helper_link_info_t link_info); + union cvmx_helper_link_info link_info); #endif diff --git a/arch/mips/include/asm/octeon/cvmx-helper-sgmii.h b/arch/mips/include/asm/octeon/cvmx-helper-sgmii.h index 3a54dea58c0a..8aac90f1871f 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper-sgmii.h +++ b/arch/mips/include/asm/octeon/cvmx-helper-sgmii.h @@ -68,7 +68,7 @@ extern int __cvmx_helper_sgmii_enable(int interface); * * Returns Link state */ -extern cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port); +extern union cvmx_helper_link_info __cvmx_helper_sgmii_link_get(int ipd_port); /** * Configure an IPD/PKO port for the specified link state. This @@ -82,6 +82,6 @@ extern cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port); * Returns Zero on success, negative on failure */ extern int __cvmx_helper_sgmii_link_set(int ipd_port, - cvmx_helper_link_info_t link_info); + union cvmx_helper_link_info link_info); #endif diff --git a/arch/mips/include/asm/octeon/cvmx-helper-spi.h b/arch/mips/include/asm/octeon/cvmx-helper-spi.h index d5adf8592773..bc8cab9367b8 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper-spi.h +++ b/arch/mips/include/asm/octeon/cvmx-helper-spi.h @@ -65,7 +65,7 @@ extern int __cvmx_helper_spi_enable(int interface); * * Returns Link state */ -extern cvmx_helper_link_info_t __cvmx_helper_spi_link_get(int ipd_port); +extern union cvmx_helper_link_info __cvmx_helper_spi_link_get(int ipd_port); /** * Configure an IPD/PKO port for the specified link state. This @@ -79,6 +79,6 @@ extern cvmx_helper_link_info_t __cvmx_helper_spi_link_get(int ipd_port); * Returns Zero on success, negative on failure */ extern int __cvmx_helper_spi_link_set(int ipd_port, - cvmx_helper_link_info_t link_info); + union cvmx_helper_link_info link_info); #endif diff --git a/arch/mips/include/asm/octeon/cvmx-helper-xaui.h b/arch/mips/include/asm/octeon/cvmx-helper-xaui.h index 51f45b495680..c18da2eba78e 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper-xaui.h +++ b/arch/mips/include/asm/octeon/cvmx-helper-xaui.h @@ -68,7 +68,7 @@ extern int __cvmx_helper_xaui_enable(int interface); * * Returns Link state */ -extern cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port); +extern union cvmx_helper_link_info __cvmx_helper_xaui_link_get(int ipd_port); /** * Configure an IPD/PKO port for the specified link state. This @@ -82,6 +82,6 @@ extern cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port); * Returns Zero on success, negative on failure */ extern int __cvmx_helper_xaui_link_set(int ipd_port, - cvmx_helper_link_info_t link_info); + union cvmx_helper_link_info link_info); #endif diff --git a/arch/mips/include/asm/octeon/cvmx-helper.h b/arch/mips/include/asm/octeon/cvmx-helper.h index ba0e76f578e0..c6c99e28eefb 100644 --- a/arch/mips/include/asm/octeon/cvmx-helper.h +++ b/arch/mips/include/asm/octeon/cvmx-helper.h @@ -51,7 +51,7 @@ typedef enum { CVMX_HELPER_INTERFACE_MODE_LOOP, } cvmx_helper_interface_mode_t; -typedef union { +union cvmx_helper_link_info { uint64_t u64; struct { uint64_t reserved_20_63:44; @@ -59,7 +59,7 @@ typedef union { uint64_t full_duplex:1; /**< 1 if the link is full duplex */ uint64_t speed:18; /**< Speed of the link in Mbps */ } s; -} cvmx_helper_link_info_t; +}; #include #include @@ -145,7 +145,7 @@ extern cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int * * Returns Link state */ -extern cvmx_helper_link_info_t cvmx_helper_link_get(int ipd_port); +extern union cvmx_helper_link_info cvmx_helper_link_get(int ipd_port); /** * Configure an IPD/PKO port for the specified link state. This @@ -159,7 +159,7 @@ extern cvmx_helper_link_info_t cvmx_helper_link_get(int ipd_port); * Returns Zero on success, negative on failure */ extern int cvmx_helper_link_set(int ipd_port, - cvmx_helper_link_info_t link_info); + union cvmx_helper_link_info link_info); /** * This function probes an interface to determine the actual From 9da303533b40cf02b96b66d0411723e0b48cfbfd Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 5 Feb 2020 13:11:15 +1300 Subject: [PATCH 0158/2231] MIPS: octeon: remove typedef declaration for cvmx_pko_command_word0 Remove the typedef and update usage to use the union. Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20200205001116.14096-6-chris.packham@alliedtelesis.co.nz Signed-off-by: Greg Kroah-Hartman --- arch/mips/cavium-octeon/executive/cvmx-helper.c | 2 +- arch/mips/include/asm/octeon/cvmx-pko.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper.c b/arch/mips/cavium-octeon/executive/cvmx-helper.c index 8002f9d3039d..6044ff471002 100644 --- a/arch/mips/cavium-octeon/executive/cvmx-helper.c +++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c @@ -782,7 +782,7 @@ static int __cvmx_helper_errata_fix_ipd_ptr_alignment(void) #define INTERFACE(port) (port >> 4) #define INDEX(port) (port & 0xf) uint64_t *p64; - cvmx_pko_command_word0_t pko_command; + union cvmx_pko_command_word0 pko_command; union cvmx_buf_ptr g_buffer, pkt_buffer; struct cvmx_wqe *work; int size, num_segs = 0, wqe_pcnt, pkt_pcnt; diff --git a/arch/mips/include/asm/octeon/cvmx-pko.h b/arch/mips/include/asm/octeon/cvmx-pko.h index f033523c19f8..5b0b982a36f7 100644 --- a/arch/mips/include/asm/octeon/cvmx-pko.h +++ b/arch/mips/include/asm/octeon/cvmx-pko.h @@ -169,7 +169,7 @@ typedef union { /** * Structure of the first packet output command word. */ -typedef union { +union cvmx_pko_command_word0 { uint64_t u64; struct { #ifdef __BIG_ENDIAN_BITFIELD @@ -261,7 +261,7 @@ typedef union { uint64_t size1:2; #endif } s; -} cvmx_pko_command_word0_t; +}; /* CSR typedefs have been moved to cvmx-csr-*.h */ @@ -419,7 +419,7 @@ static inline void cvmx_pko_send_packet_prepare(uint64_t port, uint64_t queue, static inline cvmx_pko_status_t cvmx_pko_send_packet_finish( uint64_t port, uint64_t queue, - cvmx_pko_command_word0_t pko_command, + union cvmx_pko_command_word0 pko_command, union cvmx_buf_ptr packet, cvmx_pko_lock_t use_locking) { @@ -462,7 +462,7 @@ static inline cvmx_pko_status_t cvmx_pko_send_packet_finish( static inline cvmx_pko_status_t cvmx_pko_send_packet_finish3( uint64_t port, uint64_t queue, - cvmx_pko_command_word0_t pko_command, + union cvmx_pko_command_word0 pko_command, union cvmx_buf_ptr packet, uint64_t addr, cvmx_pko_lock_t use_locking) From 8ef0c4f06432b5b2f4e32f37b81708eae1e20c8c Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 5 Feb 2020 13:11:16 +1300 Subject: [PATCH 0159/2231] Revert "staging/octeon: Mark Ethernet driver as BROKEN" This reverts commit 075a1e87d1e2358d0b0301ac8f8e7f25051decf1. Now that the build issues have been fixed we can resume build testing. Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20200205001116.14096-7-chris.packham@alliedtelesis.co.nz Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig index e7f4ddcc1361..5319909eb2f6 100644 --- a/drivers/staging/octeon/Kconfig +++ b/drivers/staging/octeon/Kconfig @@ -3,7 +3,6 @@ config OCTEON_ETHERNET tristate "Cavium Networks Octeon Ethernet support" depends on CAVIUM_OCTEON_SOC || COMPILE_TEST depends on NETDEVICES - depends on BROKEN select PHYLIB select MDIO_OCTEON help From 23a653eb1f3f16755720dde406f137441add88d0 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 17:47:10 -0600 Subject: [PATCH 0160/2231] regulator: da9063: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200211234710.GA29532@embeddedor Signed-off-by: Mark Brown --- drivers/regulator/da9063-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index aaa994293e9b..44727704d3c5 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -152,7 +152,7 @@ struct da9063_regulator { struct da9063_regulators { unsigned int n_regulators; /* Array size to be defined during init. Keep at end. */ - struct da9063_regulator regulator[0]; + struct da9063_regulator regulator[]; }; /* BUCK modes for DA9063 */ From d69f763fd360ec62dab9024f98209655f3046c68 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 17:46:12 -0600 Subject: [PATCH 0161/2231] regulator: da9062: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Acked-by: Adam Thomson Link: https://lore.kernel.org/r/20200211234612.GA28682@embeddedor Signed-off-by: Mark Brown --- drivers/regulator/da9062-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c index d3ce0278bfbe..d8112f56e94e 100644 --- a/drivers/regulator/da9062-regulator.c +++ b/drivers/regulator/da9062-regulator.c @@ -73,7 +73,7 @@ struct da9062_regulators { int irq_ldo_lim; unsigned n_regulators; /* Array size to be defined during init. Keep at end. */ - struct da9062_regulator regulator[0]; + struct da9062_regulator regulator[]; }; /* Regulator operations */ From d548ed71cb8862c96a1a8d17861bb5dabd1e2299 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 4 Feb 2020 16:28:37 +1300 Subject: [PATCH 0162/2231] dt-bindings: spi: Document binding for generic SPI multiplexer Add binding documentation for the spi-mux driver. This allows a generic multiplexer to be used to provide access to multiple SPI devices. Signed-off-by: Chris Packham Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200204032838.20739-2-chris.packham@alliedtelesis.co.nz Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-mux.yaml | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-mux.yaml diff --git a/Documentation/devicetree/bindings/spi/spi-mux.yaml b/Documentation/devicetree/bindings/spi/spi-mux.yaml new file mode 100644 index 000000000000..0ae692dc28b5 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-mux.yaml @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/spi-mux.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic SPI Multiplexer + +description: | + This binding describes a SPI bus multiplexer to route the SPI chip select + signals. This can be used when you need more devices than the SPI controller + has chip selects available. An example setup is shown in ASCII art; the actual + setting of the multiplexer to a channel needs to be done by a specific SPI mux + driver. + + MOSI /--------------------------------+--------+--------+--------\ + MISO |/------------------------------+|-------+|-------+|-------\| + SCL ||/----------------------------+||------+||------+||------\|| + ||| ||| ||| ||| ||| + +------------+ ||| ||| ||| ||| + | SoC ||| | +-+++-+ +-+++-+ +-+++-+ +-+++-+ + | ||| | | dev | | dev | | dev | | dev | + | +--+++-+ | CS-X +------+\ +--+--+ +--+--+ +--+--+ +--+--+ + | | SPI +-|-------+ Mux |\\ CS-0 | | | | + | +------+ | +--+---+\\\-------/ CS-1 | | | + | | | \\\----------------/ CS-2 | | + | +------+ | | \\-------------------------/ CS-3 | + | | ? +-|----------/ \----------------------------------/ + | +------+ | + +------------+ + +allOf: + - $ref: "/schemas/spi/spi-controller.yaml#" + +maintainers: + - Chris Packham + +properties: + compatible: + const: spi-mux + + mux-controls: + maxItems: 1 + +required: + - compatible + - reg + - spi-max-frequency + - mux-controls + +examples: + - | + #include + mux: mux-controller { + compatible = "gpio-mux"; + #mux-control-cells = <0>; + + mux-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; + }; + + spi { + #address-cells = <1>; + #size-cells = <0>; + spi@0 { + compatible = "spi-mux"; + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <100000000>; + + mux-controls = <&mux>; + + spi-flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <40000000>; + }; + + spi-device@1 { + compatible = "lineartechnology,ltc2488"; + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <10000000>; + }; + }; + }; From e9e40543ad5b38b848879768359fd13650529961 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 4 Feb 2020 16:28:38 +1300 Subject: [PATCH 0163/2231] spi: Add generic SPI multiplexer Add a SPI device driver that sits in-band and provides a SPI controller which supports chip selects via a mux-control. This enables extra SPI devices to be connected with limited native chip selects. Signed-off-by: Chris Packham Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200204032838.20739-3-chris.packham@alliedtelesis.co.nz Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 11 +++ drivers/spi/Makefile | 1 + drivers/spi/spi-mux.c | 187 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 drivers/spi/spi-mux.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 0434614d8201..887fefe87fd5 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -897,6 +897,17 @@ config SPI_ZYNQMP_GQSPI # Add new SPI master controllers in alphabetical order above this line # +comment "SPI Multiplexer support" + +config SPI_MUX + tristate "SPI multiplexer support" + select MULTIPLEXER + help + This adds support for SPI multiplexers. Each SPI mux will be + accessible as a SPI controller, the devices behind the mux will appear + to be chip selects on this controller. It is still necessary to + select one or more specific mux-controller drivers. + # # There are lots of SPI device types, with sensors and memory # being probably the most widely used ones. diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9892bdfab15e..74db1f2c3299 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -9,6 +9,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG # config declarations into driver model code obj-$(CONFIG_SPI_MASTER) += spi.o obj-$(CONFIG_SPI_MEM) += spi-mem.o +obj-$(CONFIG_SPI_MUX) += spi-mux.o obj-$(CONFIG_SPI_SPIDEV) += spidev.o obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c new file mode 100644 index 000000000000..4f94c9127fc1 --- /dev/null +++ b/drivers/spi/spi-mux.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// General Purpose SPI multiplexer + +#include +#include +#include +#include +#include +#include + +#define SPI_MUX_NO_CS ((unsigned int)-1) + +/** + * DOC: Driver description + * + * This driver supports a MUX on an SPI bus. This can be useful when you need + * more chip selects than the hardware peripherals support, or than are + * available in a particular board setup. + * + * The driver will create an additional SPI controller. Devices added under the + * mux will be handled as 'chip selects' on this controller. + */ + +/** + * struct spi_mux_priv - the basic spi_mux structure + * @spi: pointer to the device struct attached to the parent + * spi controller + * @current_cs: The current chip select set in the mux + * @child_msg_complete: The mux replaces the complete callback in the child's + * message to its own callback; this field is used by the + * driver to store the child's callback during a transfer + * @child_msg_context: Used to store the child's context to the callback + * @child_msg_dev: Used to store the spi_device pointer to the child + * @mux: mux_control structure used to provide chip selects for + * downstream spi devices + */ +struct spi_mux_priv { + struct spi_device *spi; + unsigned int current_cs; + + void (*child_msg_complete)(void *context); + void *child_msg_context; + struct spi_device *child_msg_dev; + struct mux_control *mux; +}; + +/* should not get called when the parent controller is doing a transfer */ +static int spi_mux_select(struct spi_device *spi) +{ + struct spi_mux_priv *priv = spi_controller_get_devdata(spi->controller); + int ret; + + if (priv->current_cs == spi->chip_select) + return 0; + + dev_dbg(&priv->spi->dev, "setting up the mux for cs %d\n", + spi->chip_select); + + /* copy the child device's settings except for the cs */ + priv->spi->max_speed_hz = spi->max_speed_hz; + priv->spi->mode = spi->mode; + priv->spi->bits_per_word = spi->bits_per_word; + + ret = mux_control_select(priv->mux, spi->chip_select); + if (ret) + return ret; + + priv->current_cs = spi->chip_select; + + return 0; +} + +static int spi_mux_setup(struct spi_device *spi) +{ + struct spi_mux_priv *priv = spi_controller_get_devdata(spi->controller); + + /* + * can be called multiple times, won't do a valid setup now but we will + * change the settings when we do a transfer (necessary because we + * can't predict from which device it will be anyway) + */ + return spi_setup(priv->spi); +} + +static void spi_mux_complete_cb(void *context) +{ + struct spi_mux_priv *priv = (struct spi_mux_priv *)context; + struct spi_controller *ctlr = spi_get_drvdata(priv->spi); + struct spi_message *m = ctlr->cur_msg; + + m->complete = priv->child_msg_complete; + m->context = priv->child_msg_context; + m->spi = priv->child_msg_dev; + spi_finalize_current_message(ctlr); + mux_control_deselect(priv->mux); +} + +static int spi_mux_transfer_one_message(struct spi_controller *ctlr, + struct spi_message *m) +{ + struct spi_mux_priv *priv = spi_controller_get_devdata(ctlr); + struct spi_device *spi = m->spi; + int ret; + + ret = spi_mux_select(spi); + if (ret) + return ret; + + /* + * Replace the complete callback, context and spi_device with our own + * pointers. Save originals + */ + priv->child_msg_complete = m->complete; + priv->child_msg_context = m->context; + priv->child_msg_dev = m->spi; + + m->complete = spi_mux_complete_cb; + m->context = priv; + m->spi = priv->spi; + + /* do the transfer */ + return spi_async(priv->spi, m); +} + +static int spi_mux_probe(struct spi_device *spi) +{ + struct spi_controller *ctlr; + struct spi_mux_priv *priv; + int ret; + + ctlr = spi_alloc_master(&spi->dev, sizeof(*priv)); + if (!ctlr) + return -ENOMEM; + + spi_set_drvdata(spi, ctlr); + priv = spi_controller_get_devdata(ctlr); + priv->spi = spi; + + priv->mux = devm_mux_control_get(&spi->dev, NULL); + if (IS_ERR(priv->mux)) { + ret = PTR_ERR(priv->mux); + if (ret != -EPROBE_DEFER) + dev_err(&spi->dev, "failed to get control-mux\n"); + goto err_put_ctlr; + } + + priv->current_cs = SPI_MUX_NO_CS; + + /* supported modes are the same as our parent's */ + ctlr->mode_bits = spi->controller->mode_bits; + ctlr->flags = spi->controller->flags; + ctlr->transfer_one_message = spi_mux_transfer_one_message; + ctlr->setup = spi_mux_setup; + ctlr->num_chipselect = mux_control_states(priv->mux); + ctlr->bus_num = -1; + ctlr->dev.of_node = spi->dev.of_node; + + ret = devm_spi_register_controller(&spi->dev, ctlr); + if (ret) + goto err_put_ctlr; + + return 0; + +err_put_ctlr: + spi_controller_put(ctlr); + + return ret; +} + +static const struct of_device_id spi_mux_of_match[] = { + { .compatible = "spi-mux" }, + { } +}; + +static struct spi_driver spi_mux_driver = { + .probe = spi_mux_probe, + .driver = { + .name = "spi-mux", + .of_match_table = spi_mux_of_match, + }, +}; + +module_spi_driver(spi_mux_driver); + +MODULE_DESCRIPTION("SPI multiplexer"); +MODULE_LICENSE("GPL"); From 5a7ea52b6faec6f8877e49645a80349b2d970f0a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Feb 2020 23:58:17 +0100 Subject: [PATCH 0164/2231] PM: QoS: Drop pm_qos_update_request_timeout() The pm_qos_update_request_timeout() function is not called from anywhere, so drop it along with the work member in struct pm_qos_request needed by it. Also drop the useless pm_qos_update_request_timeout trace event that is only triggered by that function (so it never triggers at all) and update the trace events documentation accordingly. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- Documentation/trace/events-power.rst | 2 -- include/linux/pm_qos.h | 4 --- include/trace/events/power.h | 24 -------------- kernel/power/qos.c | 48 ---------------------------- 4 files changed, 78 deletions(-) diff --git a/Documentation/trace/events-power.rst b/Documentation/trace/events-power.rst index 2ef318962e29..eec7453a168e 100644 --- a/Documentation/trace/events-power.rst +++ b/Documentation/trace/events-power.rst @@ -78,11 +78,9 @@ target/flags update. pm_qos_add_request "pm_qos_class=%s value=%d" pm_qos_update_request "pm_qos_class=%s value=%d" pm_qos_remove_request "pm_qos_class=%s value=%d" - pm_qos_update_request_timeout "pm_qos_class=%s value=%d, timeout_us=%ld" The first parameter gives the QoS class name (e.g. "CPU_DMA_LATENCY"). The second parameter is value to be added/updated/removed. -The third parameter is timeout value in usec. :: pm_qos_update_target "action=%s prev_value=%d curr_value=%d" diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 19eafca5680e..4747bdb6bed2 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -8,7 +8,6 @@ #include #include #include -#include enum { PM_QOS_RESERVED = 0, @@ -43,7 +42,6 @@ enum pm_qos_flags_status { struct pm_qos_request { struct plist_node node; int pm_qos_class; - struct delayed_work work; /* for pm_qos_update_request_timeout */ }; struct pm_qos_flags_request { @@ -149,8 +147,6 @@ void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value); void pm_qos_update_request(struct pm_qos_request *req, s32 new_value); -void pm_qos_update_request_timeout(struct pm_qos_request *req, - s32 new_value, unsigned long timeout_us); void pm_qos_remove_request(struct pm_qos_request *req); int pm_qos_request(int pm_qos_class); diff --git a/include/trace/events/power.h b/include/trace/events/power.h index 7457e238e1b7..ecf39daabf16 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -404,30 +404,6 @@ DEFINE_EVENT(pm_qos_request, pm_qos_remove_request, TP_ARGS(pm_qos_class, value) ); -TRACE_EVENT(pm_qos_update_request_timeout, - - TP_PROTO(int pm_qos_class, s32 value, unsigned long timeout_us), - - TP_ARGS(pm_qos_class, value, timeout_us), - - TP_STRUCT__entry( - __field( int, pm_qos_class ) - __field( s32, value ) - __field( unsigned long, timeout_us ) - ), - - TP_fast_assign( - __entry->pm_qos_class = pm_qos_class; - __entry->value = value; - __entry->timeout_us = timeout_us; - ), - - TP_printk("pm_qos_class=%s value=%d, timeout_us=%ld", - __print_symbolic(__entry->pm_qos_class, - { PM_QOS_CPU_DMA_LATENCY, "CPU_DMA_LATENCY" }), - __entry->value, __entry->timeout_us) -); - DECLARE_EVENT_CLASS(pm_qos_update, TP_PROTO(enum pm_qos_req_action action, int prev_value, int curr_value), diff --git a/kernel/power/qos.c b/kernel/power/qos.c index d932fa42e8e4..67dab7f330e4 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -295,21 +295,6 @@ static void __pm_qos_update_request(struct pm_qos_request *req, &req->node, PM_QOS_UPDATE_REQ, new_value); } -/** - * pm_qos_work_fn - the timeout handler of pm_qos_update_request_timeout - * @work: work struct for the delayed work (timeout) - * - * This cancels the timeout request by falling back to the default at timeout. - */ -static void pm_qos_work_fn(struct work_struct *work) -{ - struct pm_qos_request *req = container_of(to_delayed_work(work), - struct pm_qos_request, - work); - - __pm_qos_update_request(req, PM_QOS_DEFAULT_VALUE); -} - /** * pm_qos_add_request - inserts new qos request into the list * @req: pointer to a preallocated handle @@ -334,7 +319,6 @@ void pm_qos_add_request(struct pm_qos_request *req, return; } req->pm_qos_class = pm_qos_class; - INIT_DELAYED_WORK(&req->work, pm_qos_work_fn); trace_pm_qos_add_request(pm_qos_class, value); pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, &req->node, PM_QOS_ADD_REQ, value); @@ -362,40 +346,10 @@ void pm_qos_update_request(struct pm_qos_request *req, return; } - cancel_delayed_work_sync(&req->work); __pm_qos_update_request(req, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); -/** - * pm_qos_update_request_timeout - modifies an existing qos request temporarily. - * @req : handle to list element holding a pm_qos request to use - * @new_value: defines the temporal qos request - * @timeout_us: the effective duration of this qos request in usecs. - * - * After timeout_us, this qos request is cancelled automatically. - */ -void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value, - unsigned long timeout_us) -{ - if (!req) - return; - if (WARN(!pm_qos_request_active(req), - "%s called for unknown object.", __func__)) - return; - - cancel_delayed_work_sync(&req->work); - - trace_pm_qos_update_request_timeout(req->pm_qos_class, - new_value, timeout_us); - if (new_value != req->node.prio) - pm_qos_update_target( - pm_qos_array[req->pm_qos_class]->constraints, - &req->node, PM_QOS_UPDATE_REQ, new_value); - - schedule_delayed_work(&req->work, usecs_to_jiffies(timeout_us)); -} - /** * pm_qos_remove_request - modifies an existing qos request * @req: handle to request list element @@ -415,8 +369,6 @@ void pm_qos_remove_request(struct pm_qos_request *req) return; } - cancel_delayed_work_sync(&req->work); - trace_pm_qos_remove_request(req->pm_qos_class, PM_QOS_DEFAULT_VALUE); pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints, &req->node, PM_QOS_REMOVE_REQ, From 87ad7356799622b69478076cd99df7a5024992cb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Feb 2020 23:58:26 +0100 Subject: [PATCH 0165/2231] PM: QoS: Drop the PM_QOS_SUM QoS type The PM_QOS_SUM QoS type is not used, so drop it along with the code referring to it in pm_qos_get_value() and the related local variables in there. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 1 - kernel/power/qos.c | 9 --------- 2 files changed, 10 deletions(-) diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 4747bdb6bed2..48bfb96a9360 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -53,7 +53,6 @@ enum pm_qos_type { PM_QOS_UNITIALIZED, PM_QOS_MAX, /* return the largest value */ PM_QOS_MIN, /* return the smallest value */ - PM_QOS_SUM /* return the sum */ }; /* diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 67dab7f330e4..a6be7faa1974 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -101,9 +101,6 @@ static const struct file_operations pm_qos_power_fops = { /* unlocked internal variant */ static inline int pm_qos_get_value(struct pm_qos_constraints *c) { - struct plist_node *node; - int total_value = 0; - if (plist_head_empty(&c->list)) return c->no_constraint_value; @@ -114,12 +111,6 @@ static inline int pm_qos_get_value(struct pm_qos_constraints *c) case PM_QOS_MAX: return plist_last(&c->list)->prio; - case PM_QOS_SUM: - plist_for_each(node, &c->list) - total_value += node->prio; - - return total_value; - default: /* runtime check for not using enum */ BUG(); From 7b35370b2ebc2608ba0f2e0ecb996fa7f38e9731 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Feb 2020 23:58:33 +0100 Subject: [PATCH 0166/2231] PM: QoS: Clean up pm_qos_update_target() and pm_qos_update_flags() Clean up the pm_qos_update_target() function: * Update its kerneldoc comment. * Drop the redundant ret local variable from it. * Reorder definitions of local variables in it. * Update a comment in it. Also update the kerneldoc comment of pm_qos_update_flags() (e.g. notifiers are not called by it any more) and add one emtpy line to its body (for more visual clarity). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- kernel/power/qos.c | 56 ++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/kernel/power/qos.c b/kernel/power/qos.c index a6be7faa1974..6a36809d6160 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -129,24 +129,30 @@ static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value) } /** - * pm_qos_update_target - manages the constraints list and calls the notifiers - * if needed - * @c: constraints data struct - * @node: request to add to the list, to update or to remove - * @action: action to take on the constraints list - * @value: value of the request to add or update + * pm_qos_update_target - Update a list of PM QoS constraint requests. + * @c: List of PM QoS requests. + * @node: Target list entry. + * @action: Action to carry out (add, update or remove). + * @value: New request value for the target list entry. * - * This function returns 1 if the aggregated constraint value has changed, 0 - * otherwise. + * Update the given list of PM QoS constraint requests, @c, by carrying an + * @action involving the @node list entry and @value on it. + * + * The recognized values of @action are PM_QOS_ADD_REQ (store @value in @node + * and add it to the list), PM_QOS_UPDATE_REQ (remove @node from the list, store + * @value in it and add it to the list again), and PM_QOS_REMOVE_REQ (remove + * @node from the list, ignore @value). + * + * Return: 1 if the aggregate constraint value has changed, 0 otherwise. */ int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, enum pm_qos_req_action action, int value) { - unsigned long flags; int prev_value, curr_value, new_value; - int ret; + unsigned long flags; spin_lock_irqsave(&pm_qos_lock, flags); + prev_value = pm_qos_get_value(c); if (value == PM_QOS_DEFAULT_VALUE) new_value = c->default_value; @@ -159,9 +165,8 @@ int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, break; case PM_QOS_UPDATE_REQ: /* - * to change the list, we atomically remove, reinit - * with new value and add, then see if the extremal - * changed + * To change the list, atomically remove, reinit with new value + * and add, then see if the aggregate has changed. */ plist_del(node, &c->list); /* fall through */ @@ -180,16 +185,14 @@ int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, spin_unlock_irqrestore(&pm_qos_lock, flags); trace_pm_qos_update_target(action, prev_value, curr_value); - if (prev_value != curr_value) { - ret = 1; - if (c->notifiers) - blocking_notifier_call_chain(c->notifiers, - (unsigned long)curr_value, - NULL); - } else { - ret = 0; - } - return ret; + + if (prev_value == curr_value) + return 0; + + if (c->notifiers) + blocking_notifier_call_chain(c->notifiers, curr_value, NULL); + + return 1; } /** @@ -211,14 +214,12 @@ static void pm_qos_flags_remove_req(struct pm_qos_flags *pqf, /** * pm_qos_update_flags - Update a set of PM QoS flags. - * @pqf: Set of flags to update. + * @pqf: Set of PM QoS flags to update. * @req: Request to add to the set, to modify, or to remove from the set. * @action: Action to take on the set. * @val: Value of the request to add or modify. * - * Update the given set of PM QoS flags and call notifiers if the aggregate - * value has changed. Returns 1 if the aggregate constraint value has changed, - * 0 otherwise. + * Return: 1 if the aggregate constraint value has changed, 0 otherwise. */ bool pm_qos_update_flags(struct pm_qos_flags *pqf, struct pm_qos_flags_request *req, @@ -254,6 +255,7 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, spin_unlock_irqrestore(&pm_qos_lock, irqflags); trace_pm_qos_update_flags(action, prev_value, curr_value); + return prev_value != curr_value; } From dcd70ca1a3bf33aa5cf53aa6f72b8e51afb1ac48 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Feb 2020 23:58:39 +0100 Subject: [PATCH 0167/2231] PM: QoS: Clean up pm_qos_read_value() and pm_qos_get/set_value() Move the definition of pm_qos_read_value() before the one of pm_qos_get_value() and add a kerneldoc comment to it (as it is not static). Also replace the BUG() in pm_qos_get_value() with WARN() (to prevent the kernel from crashing if an unknown PM QoS type is used by mistake) and drop the comment next to it that is not necessary any more. Additionally, drop the unnecessary inline modifier from the header of pm_qos_set_value(). Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- kernel/power/qos.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 6a36809d6160..f09eca5ffe07 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -98,8 +98,16 @@ static const struct file_operations pm_qos_power_fops = { .llseek = noop_llseek, }; -/* unlocked internal variant */ -static inline int pm_qos_get_value(struct pm_qos_constraints *c) +/** + * pm_qos_read_value - Return the current effective constraint value. + * @c: List of PM QoS constraint requests. + */ +s32 pm_qos_read_value(struct pm_qos_constraints *c) +{ + return c->target_value; +} + +static int pm_qos_get_value(struct pm_qos_constraints *c) { if (plist_head_empty(&c->list)) return c->no_constraint_value; @@ -112,18 +120,12 @@ static inline int pm_qos_get_value(struct pm_qos_constraints *c) return plist_last(&c->list)->prio; default: - /* runtime check for not using enum */ - BUG(); + WARN(1, "Unknown PM QoS type in %s\n", __func__); return PM_QOS_DEFAULT_VALUE; } } -s32 pm_qos_read_value(struct pm_qos_constraints *c) -{ - return c->target_value; -} - -static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value) +static void pm_qos_set_value(struct pm_qos_constraints *c, s32 value) { c->target_value = value; } From 63cffc05348ea1fe65aa99e80ed330e33bf54220 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Feb 2020 23:59:22 +0100 Subject: [PATCH 0168/2231] PM: QoS: Drop iterations over global QoS classes After commit c3082a674f46 ("PM: QoS: Get rid of unused flags") the only global PM QoS class in use is PM_QOS_CPU_DMA_LATENCY, so it does not really make sense to iterate over global QoS classes anywhere, since there is only one. Remove iterations over global QoS classes from the code and use PM_QOS_CPU_DMA_LATENCY as the target class directly where needed. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- kernel/power/qos.c | 52 +++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/kernel/power/qos.c b/kernel/power/qos.c index f09eca5ffe07..57ff542a4f9d 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -412,7 +412,8 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) } EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); -/* User space interface to PM QoS classes via misc devices */ +/* User space interface to global PM QoS via misc device. */ + static int register_pm_qos_misc(struct pm_qos_object *qos) { qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; @@ -422,35 +423,18 @@ static int register_pm_qos_misc(struct pm_qos_object *qos) return misc_register(&qos->pm_qos_power_miscdev); } -static int find_pm_qos_object_by_minor(int minor) -{ - int pm_qos_class; - - for (pm_qos_class = PM_QOS_CPU_DMA_LATENCY; - pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) { - if (minor == - pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor) - return pm_qos_class; - } - return -1; -} - static int pm_qos_power_open(struct inode *inode, struct file *filp) { - long pm_qos_class; + struct pm_qos_request *req; - pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); - if (pm_qos_class >= PM_QOS_CPU_DMA_LATENCY) { - struct pm_qos_request *req = kzalloc(sizeof(*req), GFP_KERNEL); - if (!req) - return -ENOMEM; + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; - pm_qos_add_request(req, pm_qos_class, PM_QOS_DEFAULT_VALUE); - filp->private_data = req; + pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + filp->private_data = req; - return 0; - } - return -EPERM; + return 0; } static int pm_qos_power_release(struct inode *inode, struct file *filp) @@ -464,7 +448,6 @@ static int pm_qos_power_release(struct inode *inode, struct file *filp) return 0; } - static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { @@ -507,26 +490,19 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, return count; } - static int __init pm_qos_power_init(void) { - int ret = 0; - int i; + int ret; BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES); - for (i = PM_QOS_CPU_DMA_LATENCY; i < PM_QOS_NUM_CLASSES; i++) { - ret = register_pm_qos_misc(pm_qos_array[i]); - if (ret < 0) { - pr_err("%s: %s setup failed\n", - __func__, pm_qos_array[i]->name); - return ret; - } - } + ret = register_pm_qos_misc(pm_qos_array[PM_QOS_CPU_DMA_LATENCY]); + if (ret < 0) + pr_err("%s: %s setup failed\n", __func__, + pm_qos_array[PM_QOS_CPU_DMA_LATENCY]->name); return ret; } - late_initcall(pm_qos_power_init); /* Definitions related to the frequency QoS below. */ From 299a229830a29a2d691ac17b27964c7d820f16a6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:00:12 +0100 Subject: [PATCH 0169/2231] PM: QoS: Clean up misc device file operations Reorder the code to avoid using extra function header declarations for the pm_qos_power_*() family of functions and drop those declarations. Also clean up the internals of those functions to consolidate checks, avoid using redundant local variables and similar. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- kernel/power/qos.c | 62 +++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 57ff542a4f9d..9f67584d4466 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -83,21 +83,6 @@ static struct pm_qos_object *pm_qos_array[] = { &cpu_dma_pm_qos, }; -static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos); -static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, - size_t count, loff_t *f_pos); -static int pm_qos_power_open(struct inode *inode, struct file *filp); -static int pm_qos_power_release(struct inode *inode, struct file *filp); - -static const struct file_operations pm_qos_power_fops = { - .write = pm_qos_power_write, - .read = pm_qos_power_read, - .open = pm_qos_power_open, - .release = pm_qos_power_release, - .llseek = noop_llseek, -}; - /** * pm_qos_read_value - Return the current effective constraint value. * @c: List of PM QoS constraint requests. @@ -414,15 +399,6 @@ EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); /* User space interface to global PM QoS via misc device. */ -static int register_pm_qos_misc(struct pm_qos_object *qos) -{ - qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; - qos->pm_qos_power_miscdev.name = qos->name; - qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; - - return misc_register(&qos->pm_qos_power_miscdev); -} - static int pm_qos_power_open(struct inode *inode, struct file *filp) { struct pm_qos_request *req; @@ -439,9 +415,10 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp) static int pm_qos_power_release(struct inode *inode, struct file *filp) { - struct pm_qos_request *req; + struct pm_qos_request *req = filp->private_data; + + filp->private_data = NULL; - req = filp->private_data; pm_qos_remove_request(req); kfree(req); @@ -449,15 +426,13 @@ static int pm_qos_power_release(struct inode *inode, struct file *filp) } static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, - size_t count, loff_t *f_pos) + size_t count, loff_t *f_pos) { - s32 value; - unsigned long flags; struct pm_qos_request *req = filp->private_data; + unsigned long flags; + s32 value; - if (!req) - return -EINVAL; - if (!pm_qos_request_active(req)) + if (!req || !pm_qos_request_active(req)) return -EINVAL; spin_lock_irqsave(&pm_qos_lock, flags); @@ -468,10 +443,9 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, } static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos) + size_t count, loff_t *f_pos) { s32 value; - struct pm_qos_request *req; if (count == sizeof(s32)) { if (copy_from_user(&value, buf, sizeof(s32))) @@ -484,12 +458,28 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, return ret; } - req = filp->private_data; - pm_qos_update_request(req, value); + pm_qos_update_request(filp->private_data, value); return count; } +static const struct file_operations pm_qos_power_fops = { + .write = pm_qos_power_write, + .read = pm_qos_power_read, + .open = pm_qos_power_open, + .release = pm_qos_power_release, + .llseek = noop_llseek, +}; + +static int register_pm_qos_misc(struct pm_qos_object *qos) +{ + qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; + qos->pm_qos_power_miscdev.name = qos->name; + qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; + + return misc_register(&qos->pm_qos_power_miscdev); +} + static int __init pm_qos_power_init(void) { int ret; From 02c92a378940ba1a46b6b7ad277f1b07f8093dbc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:01:18 +0100 Subject: [PATCH 0170/2231] PM: QoS: Redefine struct pm_qos_request and drop struct pm_qos_object First, change the definition of struct pm_qos_request so that it contains a struct pm_qos_constraints pointer (called "qos") instead of a PM QoS class number (in preparation for dropping the PM QoS classes concept altogether going forward) and move its definition (along with the definition of struct pm_qos_flags_request that does not change) after the definition of struct pm_qos_constraints. Next, drop the definition of struct pm_qos_object and the null_pm_qos and cpu_dma_pm_qos variables of that type along with pm_qos_array[] holding pointers to them and change the code to refer to the pm_qos_constraints structure directly or to use the new qos pointer in struct pm_qos_request for that instead of going through pm_qos_array[] to access it. Also update kerneldoc comments that mention pm_qos_class to refer to PM_QOS_CPU_DMA_LATENCY directly instead. Finally, drop register_pm_qos_misc(), introduce cpu_latency_qos_miscdev (with the name field set to "cpu_dma_latency") to implement the CPU latency QoS interface in /dev/ and register it directly from pm_qos_power_init(). After these changes the notion of PM QoS classes remains only in the API (in the form of redundant function parameters that are ignored) and in the definitions of PM QoS trace events. While at it, some redundant local variables are dropped etc. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 20 +++---- kernel/power/qos.c | 121 ++++++++++++++--------------------------- 2 files changed, 51 insertions(+), 90 deletions(-) diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 48bfb96a9360..bef110aa80cc 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -39,16 +39,6 @@ enum pm_qos_flags_status { #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) -struct pm_qos_request { - struct plist_node node; - int pm_qos_class; -}; - -struct pm_qos_flags_request { - struct list_head node; - s32 flags; /* Do not change to 64 bit */ -}; - enum pm_qos_type { PM_QOS_UNITIALIZED, PM_QOS_MAX, /* return the largest value */ @@ -69,6 +59,16 @@ struct pm_qos_constraints { struct blocking_notifier_head *notifiers; }; +struct pm_qos_request { + struct plist_node node; + struct pm_qos_constraints *qos; +}; + +struct pm_qos_flags_request { + struct list_head node; + s32 flags; /* Do not change to 64 bit */ +}; + struct pm_qos_flags { struct list_head list; s32 effective_flags; /* Do not change to 64 bit */ diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 9f67584d4466..952c5f55e23c 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -54,16 +54,8 @@ * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock * held, taken with _irqsave. One lock to rule them all */ -struct pm_qos_object { - struct pm_qos_constraints *constraints; - struct miscdevice pm_qos_power_miscdev; - char *name; -}; - static DEFINE_SPINLOCK(pm_qos_lock); -static struct pm_qos_object null_pm_qos; - static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); static struct pm_qos_constraints cpu_dma_constraints = { .list = PLIST_HEAD_INIT(cpu_dma_constraints.list), @@ -73,15 +65,6 @@ static struct pm_qos_constraints cpu_dma_constraints = { .type = PM_QOS_MIN, .notifiers = &cpu_dma_lat_notifier, }; -static struct pm_qos_object cpu_dma_pm_qos = { - .constraints = &cpu_dma_constraints, - .name = "cpu_dma_latency", -}; - -static struct pm_qos_object *pm_qos_array[] = { - &null_pm_qos, - &cpu_dma_pm_qos, -}; /** * pm_qos_read_value - Return the current effective constraint value. @@ -248,46 +231,34 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, /** * pm_qos_request - returns current system wide qos expectation - * @pm_qos_class: identification of which qos value is requested + * @pm_qos_class: Ignored. * * This function returns the current target value. */ int pm_qos_request(int pm_qos_class) { - return pm_qos_read_value(pm_qos_array[pm_qos_class]->constraints); + return pm_qos_read_value(&cpu_dma_constraints); } EXPORT_SYMBOL_GPL(pm_qos_request); int pm_qos_request_active(struct pm_qos_request *req) { - return req->pm_qos_class != 0; + return req->qos == &cpu_dma_constraints; } EXPORT_SYMBOL_GPL(pm_qos_request_active); -static void __pm_qos_update_request(struct pm_qos_request *req, - s32 new_value) -{ - trace_pm_qos_update_request(req->pm_qos_class, new_value); - - if (new_value != req->node.prio) - pm_qos_update_target( - pm_qos_array[req->pm_qos_class]->constraints, - &req->node, PM_QOS_UPDATE_REQ, new_value); -} - /** * pm_qos_add_request - inserts new qos request into the list * @req: pointer to a preallocated handle - * @pm_qos_class: identifies which list of qos request to use + * @pm_qos_class: Ignored. * @value: defines the qos request * - * This function inserts a new entry in the pm_qos_class list of requested qos - * performance characteristics. It recomputes the aggregate QoS expectations - * for the pm_qos_class of parameters and initializes the pm_qos_request + * This function inserts a new entry in the PM_QOS_CPU_DMA_LATENCY list of + * requested QoS performance characteristics. It recomputes the aggregate QoS + * expectations for the PM_QOS_CPU_DMA_LATENCY list and initializes the @req * handle. Caller needs to save this handle for later use in updates and * removal. */ - void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value) { @@ -298,10 +269,11 @@ void pm_qos_add_request(struct pm_qos_request *req, WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n"); return; } - req->pm_qos_class = pm_qos_class; - trace_pm_qos_add_request(pm_qos_class, value); - pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, - &req->node, PM_QOS_ADD_REQ, value); + + trace_pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, value); + + req->qos = &cpu_dma_constraints; + pm_qos_update_target(req->qos, &req->node, PM_QOS_ADD_REQ, value); } EXPORT_SYMBOL_GPL(pm_qos_add_request); @@ -310,13 +282,12 @@ EXPORT_SYMBOL_GPL(pm_qos_add_request); * @req : handle to list element holding a pm_qos request to use * @value: defines the qos request * - * Updates an existing qos request for the pm_qos_class of parameters along - * with updating the target pm_qos_class value. + * Updates an existing qos request for the PM_QOS_CPU_DMA_LATENCY list along + * with updating the target PM_QOS_CPU_DMA_LATENCY value. * * Attempts are made to make this code callable on hot code paths. */ -void pm_qos_update_request(struct pm_qos_request *req, - s32 new_value) +void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) { if (!req) /*guard against callers passing in null */ return; @@ -326,7 +297,12 @@ void pm_qos_update_request(struct pm_qos_request *req, return; } - __pm_qos_update_request(req, new_value); + trace_pm_qos_update_request(PM_QOS_CPU_DMA_LATENCY, new_value); + + if (new_value == req->node.prio) + return; + + pm_qos_update_target(req->qos, &req->node, PM_QOS_UPDATE_REQ, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); @@ -335,7 +311,7 @@ EXPORT_SYMBOL_GPL(pm_qos_update_request); * @req: handle to request list element * * Will remove pm qos request from the list of constraints and - * recompute the current target value for the pm_qos_class. Call this + * recompute the current target value for PM_QOS_CPU_DMA_LATENCY. Call this * on slow code paths. */ void pm_qos_remove_request(struct pm_qos_request *req) @@ -349,9 +325,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) return; } - trace_pm_qos_remove_request(req->pm_qos_class, PM_QOS_DEFAULT_VALUE); - pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints, - &req->node, PM_QOS_REMOVE_REQ, + trace_pm_qos_remove_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + + pm_qos_update_target(req->qos, &req->node, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } @@ -359,41 +335,31 @@ EXPORT_SYMBOL_GPL(pm_qos_remove_request); /** * pm_qos_add_notifier - sets notification entry for changes to target value - * @pm_qos_class: identifies which qos target changes should be notified. + * @pm_qos_class: Ignored. * @notifier: notifier block managed by caller. * * will register the notifier into a notification chain that gets called - * upon changes to the pm_qos_class target value. + * upon changes to the PM_QOS_CPU_DMA_LATENCY target value. */ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) { - int retval; - - retval = blocking_notifier_chain_register( - pm_qos_array[pm_qos_class]->constraints->notifiers, - notifier); - - return retval; + return blocking_notifier_chain_register(cpu_dma_constraints.notifiers, + notifier); } EXPORT_SYMBOL_GPL(pm_qos_add_notifier); /** * pm_qos_remove_notifier - deletes notification entry from chain. - * @pm_qos_class: identifies which qos target changes are notified. + * @pm_qos_class: Ignored. * @notifier: notifier block to be removed. * * will remove the notifier from the notification chain that gets called - * upon changes to the pm_qos_class target value. + * upon changes to the PM_QOS_CPU_DMA_LATENCY target value. */ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) { - int retval; - - retval = blocking_notifier_chain_unregister( - pm_qos_array[pm_qos_class]->constraints->notifiers, - notifier); - - return retval; + return blocking_notifier_chain_unregister(cpu_dma_constraints.notifiers, + notifier); } EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); @@ -436,7 +402,7 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, return -EINVAL; spin_lock_irqsave(&pm_qos_lock, flags); - value = pm_qos_get_value(pm_qos_array[req->pm_qos_class]->constraints); + value = pm_qos_get_value(&cpu_dma_constraints); spin_unlock_irqrestore(&pm_qos_lock, flags); return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); @@ -471,25 +437,20 @@ static const struct file_operations pm_qos_power_fops = { .llseek = noop_llseek, }; -static int register_pm_qos_misc(struct pm_qos_object *qos) -{ - qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; - qos->pm_qos_power_miscdev.name = qos->name; - qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; - - return misc_register(&qos->pm_qos_power_miscdev); -} +static struct miscdevice cpu_latency_qos_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cpu_dma_latency", + .fops = &pm_qos_power_fops, +}; static int __init pm_qos_power_init(void) { int ret; - BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES); - - ret = register_pm_qos_misc(pm_qos_array[PM_QOS_CPU_DMA_LATENCY]); + ret = misc_register(&cpu_latency_qos_miscdev); if (ret < 0) pr_err("%s: %s setup failed\n", __func__, - pm_qos_array[PM_QOS_CPU_DMA_LATENCY]->name); + cpu_latency_qos_miscdev.name); return ret; } From 3a4a0042228a854d9b1073050620820b4a977e6e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:02:30 +0100 Subject: [PATCH 0171/2231] PM: QoS: Drop PM_QOS_CPU_DMA_LATENCY notifier chain Notice that pm_qos_remove_notifier() is not used at all and the only caller of pm_qos_add_notifier() is the cpuidle core, which only needs the PM_QOS_CPU_DMA_LATENCY notifier to invoke wake_up_all_idle_cpus() upon changes of the PM_QOS_CPU_DMA_LATENCY target value. First, to ensure that wake_up_all_idle_cpus() will be called whenever the PM_QOS_CPU_DMA_LATENCY target value changes, modify the pm_qos_add/update/remove_request() family of functions to check if the effective constraint for the PM_QOS_CPU_DMA_LATENCY has changed and call wake_up_all_idle_cpus() directly in that case. Next, drop the PM_QOS_CPU_DMA_LATENCY notifier from cpuidle as it is not necessary any more. Finally, drop both pm_qos_add_notifier() and pm_qos_remove_notifier(), as they have no callers now, along with cpu_dma_lat_notifier which is only used by them. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/cpuidle/cpuidle.c | 40 +-------------------------------- include/linux/pm_qos.h | 2 -- kernel/power/qos.c | 47 +++++++++------------------------------ 3 files changed, 12 insertions(+), 77 deletions(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index de81298051b3..c149d9e20dfd 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -736,53 +736,15 @@ int cpuidle_register(struct cpuidle_driver *drv, } EXPORT_SYMBOL_GPL(cpuidle_register); -#ifdef CONFIG_SMP - -/* - * This function gets called when a part of the kernel has a new latency - * requirement. This means we need to get all processors out of their C-state, - * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that - * wakes them all right up. - */ -static int cpuidle_latency_notify(struct notifier_block *b, - unsigned long l, void *v) -{ - wake_up_all_idle_cpus(); - return NOTIFY_OK; -} - -static struct notifier_block cpuidle_latency_notifier = { - .notifier_call = cpuidle_latency_notify, -}; - -static inline void latency_notifier_init(struct notifier_block *n) -{ - pm_qos_add_notifier(PM_QOS_CPU_DMA_LATENCY, n); -} - -#else /* CONFIG_SMP */ - -#define latency_notifier_init(x) do { } while (0) - -#endif /* CONFIG_SMP */ - /** * cpuidle_init - core initializer */ static int __init cpuidle_init(void) { - int ret; - if (cpuidle_disabled()) return -ENODEV; - ret = cpuidle_add_interface(cpu_subsys.dev_root); - if (ret) - return ret; - - latency_notifier_init(&cpuidle_latency_notifier); - - return 0; + return cpuidle_add_interface(cpu_subsys.dev_root); } module_param(off, int, 0444); diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index bef110aa80cc..cb57e5918a25 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -149,8 +149,6 @@ void pm_qos_update_request(struct pm_qos_request *req, void pm_qos_remove_request(struct pm_qos_request *req); int pm_qos_request(int pm_qos_class); -int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier); -int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); int pm_qos_request_active(struct pm_qos_request *req); s32 pm_qos_read_value(struct pm_qos_constraints *c); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 952c5f55e23c..201b43bc6457 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -56,14 +56,12 @@ */ static DEFINE_SPINLOCK(pm_qos_lock); -static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); static struct pm_qos_constraints cpu_dma_constraints = { .list = PLIST_HEAD_INIT(cpu_dma_constraints.list), .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .no_constraint_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .type = PM_QOS_MIN, - .notifiers = &cpu_dma_lat_notifier, }; /** @@ -247,6 +245,14 @@ int pm_qos_request_active(struct pm_qos_request *req) } EXPORT_SYMBOL_GPL(pm_qos_request_active); +static void cpu_latency_qos_update(struct pm_qos_request *req, + enum pm_qos_req_action action, s32 value) +{ + int ret = pm_qos_update_target(req->qos, &req->node, action, value); + if (ret > 0) + wake_up_all_idle_cpus(); +} + /** * pm_qos_add_request - inserts new qos request into the list * @req: pointer to a preallocated handle @@ -273,7 +279,7 @@ void pm_qos_add_request(struct pm_qos_request *req, trace_pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, value); req->qos = &cpu_dma_constraints; - pm_qos_update_target(req->qos, &req->node, PM_QOS_ADD_REQ, value); + cpu_latency_qos_update(req, PM_QOS_ADD_REQ, value); } EXPORT_SYMBOL_GPL(pm_qos_add_request); @@ -302,7 +308,7 @@ void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) if (new_value == req->node.prio) return; - pm_qos_update_target(req->qos, &req->node, PM_QOS_UPDATE_REQ, new_value); + cpu_latency_qos_update(req, PM_QOS_UPDATE_REQ, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); @@ -327,42 +333,11 @@ void pm_qos_remove_request(struct pm_qos_request *req) trace_pm_qos_remove_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); - pm_qos_update_target(req->qos, &req->node, PM_QOS_REMOVE_REQ, - PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_update(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } EXPORT_SYMBOL_GPL(pm_qos_remove_request); -/** - * pm_qos_add_notifier - sets notification entry for changes to target value - * @pm_qos_class: Ignored. - * @notifier: notifier block managed by caller. - * - * will register the notifier into a notification chain that gets called - * upon changes to the PM_QOS_CPU_DMA_LATENCY target value. - */ -int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) -{ - return blocking_notifier_chain_register(cpu_dma_constraints.notifiers, - notifier); -} -EXPORT_SYMBOL_GPL(pm_qos_add_notifier); - -/** - * pm_qos_remove_notifier - deletes notification entry from chain. - * @pm_qos_class: Ignored. - * @notifier: notifier block to be removed. - * - * will remove the notifier from the notification chain that gets called - * upon changes to the PM_QOS_CPU_DMA_LATENCY target value. - */ -int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) -{ - return blocking_notifier_chain_unregister(cpu_dma_constraints.notifiers, - notifier); -} -EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); - /* User space interface to global PM QoS via misc device. */ static int pm_qos_power_open(struct inode *inode, struct file *filp) From 2552d3520132a22834e0be85c51168a7a798608c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:04:31 +0100 Subject: [PATCH 0172/2231] PM: QoS: Rename things related to the CPU latency QoS First, rename PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE to PM_QOS_CPU_LATENCY_DEFAULT_VALUE and update all of the code referring to it accordingly. Next, rename cpu_dma_constraints to cpu_latency_constraints, move the definition of it closer to the functions referring to it and update all of them accordingly. [While at it, add a comment to mark the start of the code related to the CPU latency QoS.] Finally, rename the pm_qos_power_*() family of functions and pm_qos_power_fops to cpu_latency_qos_*() and cpu_latency_qos_fops, respectively, and update the definition of cpu_latency_qos_miscdev. [While at it, update the miscdev interface code start comment.] No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Acked-by: Greg Kroah-Hartman Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/tty/serial/8250/8250_omap.c | 6 ++-- drivers/tty/serial/omap-serial.c | 6 ++-- include/linux/pm_qos.h | 2 +- kernel/power/qos.c | 56 +++++++++++++++-------------- 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 6f343ca08440..19f8d2f9e7ba 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1222,8 +1222,8 @@ static int omap8250_probe(struct platform_device *pdev) DEFAULT_CLK_SPEED); } - priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; - priv->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; + priv->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; pm_qos_add_request(&priv->pm_qos_request, PM_QOS_CPU_DMA_LATENCY, priv->latency); INIT_WORK(&priv->qos_work, omap8250_uart_qos_work); @@ -1445,7 +1445,7 @@ static int omap8250_runtime_suspend(struct device *dev) if (up->dma && up->dma->rxchan) omap_8250_rx_dma_flush(up); - priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; schedule_work(&priv->qos_work); return 0; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 48017cec7f2f..ce2558767eee 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1722,8 +1722,8 @@ static int serial_omap_probe(struct platform_device *pdev) DEFAULT_CLK_SPEED); } - up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; - up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + up->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; + up->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; pm_qos_add_request(&up->pm_qos_request, PM_QOS_CPU_DMA_LATENCY, up->latency); INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); @@ -1869,7 +1869,7 @@ static int serial_omap_runtime_suspend(struct device *dev) serial_omap_enable_wakeup(up, true); - up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + up->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; schedule_work(&up->qos_work); return 0; diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index cb57e5918a25..a3e0bfc6c470 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -28,7 +28,7 @@ enum pm_qos_flags_status { #define PM_QOS_LATENCY_ANY S32_MAX #define PM_QOS_LATENCY_ANY_NS ((s64)PM_QOS_LATENCY_ANY * NSEC_PER_USEC) -#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) +#define PM_QOS_CPU_LATENCY_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE PM_QOS_LATENCY_ANY #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 201b43bc6457..a6bf53e9db17 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -56,14 +56,6 @@ */ static DEFINE_SPINLOCK(pm_qos_lock); -static struct pm_qos_constraints cpu_dma_constraints = { - .list = PLIST_HEAD_INIT(cpu_dma_constraints.list), - .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, - .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, - .no_constraint_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, - .type = PM_QOS_MIN, -}; - /** * pm_qos_read_value - Return the current effective constraint value. * @c: List of PM QoS constraint requests. @@ -227,6 +219,16 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, return prev_value != curr_value; } +/* Definitions related to the CPU latency QoS. */ + +static struct pm_qos_constraints cpu_latency_constraints = { + .list = PLIST_HEAD_INIT(cpu_latency_constraints.list), + .target_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE, + .default_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE, + .no_constraint_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE, + .type = PM_QOS_MIN, +}; + /** * pm_qos_request - returns current system wide qos expectation * @pm_qos_class: Ignored. @@ -235,13 +237,13 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, */ int pm_qos_request(int pm_qos_class) { - return pm_qos_read_value(&cpu_dma_constraints); + return pm_qos_read_value(&cpu_latency_constraints); } EXPORT_SYMBOL_GPL(pm_qos_request); int pm_qos_request_active(struct pm_qos_request *req) { - return req->qos == &cpu_dma_constraints; + return req->qos == &cpu_latency_constraints; } EXPORT_SYMBOL_GPL(pm_qos_request_active); @@ -278,7 +280,7 @@ void pm_qos_add_request(struct pm_qos_request *req, trace_pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, value); - req->qos = &cpu_dma_constraints; + req->qos = &cpu_latency_constraints; cpu_latency_qos_update(req, PM_QOS_ADD_REQ, value); } EXPORT_SYMBOL_GPL(pm_qos_add_request); @@ -338,9 +340,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) } EXPORT_SYMBOL_GPL(pm_qos_remove_request); -/* User space interface to global PM QoS via misc device. */ +/* User space interface to the CPU latency QoS via misc device. */ -static int pm_qos_power_open(struct inode *inode, struct file *filp) +static int cpu_latency_qos_open(struct inode *inode, struct file *filp) { struct pm_qos_request *req; @@ -354,7 +356,7 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp) return 0; } -static int pm_qos_power_release(struct inode *inode, struct file *filp) +static int cpu_latency_qos_release(struct inode *inode, struct file *filp) { struct pm_qos_request *req = filp->private_data; @@ -366,8 +368,8 @@ static int pm_qos_power_release(struct inode *inode, struct file *filp) return 0; } -static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, - size_t count, loff_t *f_pos) +static ssize_t cpu_latency_qos_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) { struct pm_qos_request *req = filp->private_data; unsigned long flags; @@ -377,14 +379,14 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, return -EINVAL; spin_lock_irqsave(&pm_qos_lock, flags); - value = pm_qos_get_value(&cpu_dma_constraints); + value = pm_qos_get_value(&cpu_latency_constraints); spin_unlock_irqrestore(&pm_qos_lock, flags); return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); } -static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos) +static ssize_t cpu_latency_qos_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) { s32 value; @@ -404,21 +406,21 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, return count; } -static const struct file_operations pm_qos_power_fops = { - .write = pm_qos_power_write, - .read = pm_qos_power_read, - .open = pm_qos_power_open, - .release = pm_qos_power_release, +static const struct file_operations cpu_latency_qos_fops = { + .write = cpu_latency_qos_write, + .read = cpu_latency_qos_read, + .open = cpu_latency_qos_open, + .release = cpu_latency_qos_release, .llseek = noop_llseek, }; static struct miscdevice cpu_latency_qos_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "cpu_dma_latency", - .fops = &pm_qos_power_fops, + .fops = &cpu_latency_qos_fops, }; -static int __init pm_qos_power_init(void) +static int __init cpu_latency_qos_init(void) { int ret; @@ -429,7 +431,7 @@ static int __init pm_qos_power_init(void) return ret; } -late_initcall(pm_qos_power_init); +late_initcall(cpu_latency_qos_init); /* Definitions related to the frequency QoS below. */ From 333eed7d20069e2d80446f5fdf9ac3868b55e7b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:06:17 +0100 Subject: [PATCH 0173/2231] PM: QoS: Simplify definitions of CPU latency QoS trace events Modify the definitions of the CPU latency QoS trace events to take one argument (since PM_QOS_CPU_DMA_LATENCY is always passed as the pm_qos_class argument to them) and update the documentation of them accordingly (while at it, make it explicitly mention CPU latency QoS and relocate it after the device PM QoS trace events documentation). The names and output format of the trace events do not change to preserve user space compatibility. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- Documentation/trace/events-power.rst | 19 ++++++++------- include/trace/events/power.h | 35 ++++++++++++++-------------- kernel/power/qos.c | 16 ++++++------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Documentation/trace/events-power.rst b/Documentation/trace/events-power.rst index eec7453a168e..f45bf11fa88d 100644 --- a/Documentation/trace/events-power.rst +++ b/Documentation/trace/events-power.rst @@ -73,14 +73,6 @@ The second parameter is the power domain target state. ================ The PM QoS events are used for QoS add/update/remove request and for target/flags update. -:: - - pm_qos_add_request "pm_qos_class=%s value=%d" - pm_qos_update_request "pm_qos_class=%s value=%d" - pm_qos_remove_request "pm_qos_class=%s value=%d" - -The first parameter gives the QoS class name (e.g. "CPU_DMA_LATENCY"). -The second parameter is value to be added/updated/removed. :: pm_qos_update_target "action=%s prev_value=%d curr_value=%d" @@ -90,7 +82,7 @@ The first parameter gives the QoS action name (e.g. "ADD_REQ"). The second parameter is the previous QoS value. The third parameter is the current QoS value to update. -And, there are also events used for device PM QoS add/update/remove request. +There are also events used for device PM QoS add/update/remove request. :: dev_pm_qos_add_request "device=%s type=%s new_value=%d" @@ -101,3 +93,12 @@ The first parameter gives the device name which tries to add/update/remove QoS requests. The second parameter gives the request type (e.g. "DEV_PM_QOS_RESUME_LATENCY"). The third parameter is value to be added/updated/removed. + +And, there are events used for CPU latency QoS add/update/remove request. +:: + + pm_qos_add_request "value=%d" + pm_qos_update_request "value=%d" + pm_qos_remove_request "value=%d" + +The parameter is the value to be added/updated/removed. diff --git a/include/trace/events/power.h b/include/trace/events/power.h index ecf39daabf16..af5018aa9517 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -359,51 +359,50 @@ DEFINE_EVENT(power_domain, power_domain_target, ); /* - * The pm qos events are used for pm qos update + * CPU latency QoS events used for global CPU latency QoS list updates */ -DECLARE_EVENT_CLASS(pm_qos_request, +DECLARE_EVENT_CLASS(cpu_latency_qos_request, - TP_PROTO(int pm_qos_class, s32 value), + TP_PROTO(s32 value), - TP_ARGS(pm_qos_class, value), + TP_ARGS(value), TP_STRUCT__entry( - __field( int, pm_qos_class ) __field( s32, value ) ), TP_fast_assign( - __entry->pm_qos_class = pm_qos_class; __entry->value = value; ), - TP_printk("pm_qos_class=%s value=%d", - __print_symbolic(__entry->pm_qos_class, - { PM_QOS_CPU_DMA_LATENCY, "CPU_DMA_LATENCY" }), + TP_printk("CPU_DMA_LATENCY value=%d", __entry->value) ); -DEFINE_EVENT(pm_qos_request, pm_qos_add_request, +DEFINE_EVENT(cpu_latency_qos_request, pm_qos_add_request, - TP_PROTO(int pm_qos_class, s32 value), + TP_PROTO(s32 value), - TP_ARGS(pm_qos_class, value) + TP_ARGS(value) ); -DEFINE_EVENT(pm_qos_request, pm_qos_update_request, +DEFINE_EVENT(cpu_latency_qos_request, pm_qos_update_request, - TP_PROTO(int pm_qos_class, s32 value), + TP_PROTO(s32 value), - TP_ARGS(pm_qos_class, value) + TP_ARGS(value) ); -DEFINE_EVENT(pm_qos_request, pm_qos_remove_request, +DEFINE_EVENT(cpu_latency_qos_request, pm_qos_remove_request, - TP_PROTO(int pm_qos_class, s32 value), + TP_PROTO(s32 value), - TP_ARGS(pm_qos_class, value) + TP_ARGS(value) ); +/* + * General PM QoS events used for updates of PM QoS request lists + */ DECLARE_EVENT_CLASS(pm_qos_update, TP_PROTO(enum pm_qos_req_action action, int prev_value, int curr_value), diff --git a/kernel/power/qos.c b/kernel/power/qos.c index a6bf53e9db17..afac7010e0f2 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -247,8 +247,8 @@ int pm_qos_request_active(struct pm_qos_request *req) } EXPORT_SYMBOL_GPL(pm_qos_request_active); -static void cpu_latency_qos_update(struct pm_qos_request *req, - enum pm_qos_req_action action, s32 value) +static void cpu_latency_qos_apply(struct pm_qos_request *req, + enum pm_qos_req_action action, s32 value) { int ret = pm_qos_update_target(req->qos, &req->node, action, value); if (ret > 0) @@ -278,10 +278,10 @@ void pm_qos_add_request(struct pm_qos_request *req, return; } - trace_pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, value); + trace_pm_qos_add_request(value); req->qos = &cpu_latency_constraints; - cpu_latency_qos_update(req, PM_QOS_ADD_REQ, value); + cpu_latency_qos_apply(req, PM_QOS_ADD_REQ, value); } EXPORT_SYMBOL_GPL(pm_qos_add_request); @@ -305,12 +305,12 @@ void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) return; } - trace_pm_qos_update_request(PM_QOS_CPU_DMA_LATENCY, new_value); + trace_pm_qos_update_request(new_value); if (new_value == req->node.prio) return; - cpu_latency_qos_update(req, PM_QOS_UPDATE_REQ, new_value); + cpu_latency_qos_apply(req, PM_QOS_UPDATE_REQ, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); @@ -333,9 +333,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) return; } - trace_pm_qos_remove_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + trace_pm_qos_remove_request(PM_QOS_DEFAULT_VALUE); - cpu_latency_qos_update(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } EXPORT_SYMBOL_GPL(pm_qos_remove_request); From e033b6c175a32870a2f7cd3741c0d48c858c2d04 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:07:01 +0100 Subject: [PATCH 0174/2231] PM: QoS: Adjust pm_qos_request() signature and reorder pm_qos.h Change the return type of pm_qos_request() to be the same as the one of pm_qos_read_value() called by it internally and stop exporting it to modules (because its only caller, cpuidle, is not modular). Also move the pm_qos_read_value() header away from the CPU latency QoS API function headers in pm_qos.h (because it technically does not belong to that API). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 6 +++--- kernel/power/qos.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index a3e0bfc6c470..3c4bee29ecda 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -137,20 +137,20 @@ static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req) return req->dev != NULL; } +s32 pm_qos_read_value(struct pm_qos_constraints *c); int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, enum pm_qos_req_action action, int value); bool pm_qos_update_flags(struct pm_qos_flags *pqf, struct pm_qos_flags_request *req, enum pm_qos_req_action action, s32 val); + void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value); void pm_qos_update_request(struct pm_qos_request *req, s32 new_value); void pm_qos_remove_request(struct pm_qos_request *req); - -int pm_qos_request(int pm_qos_class); +s32 pm_qos_request(int pm_qos_class); int pm_qos_request_active(struct pm_qos_request *req); -s32 pm_qos_read_value(struct pm_qos_constraints *c); #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index afac7010e0f2..7bb55aca03bb 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -235,11 +235,10 @@ static struct pm_qos_constraints cpu_latency_constraints = { * * This function returns the current target value. */ -int pm_qos_request(int pm_qos_class) +s32 pm_qos_request(int pm_qos_class) { return pm_qos_read_value(&cpu_latency_constraints); } -EXPORT_SYMBOL_GPL(pm_qos_request); int pm_qos_request_active(struct pm_qos_request *req) { From fa048c59bf1ba13d8fe61890495577073a0ea919 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:07:42 +0100 Subject: [PATCH 0175/2231] PM: QoS: Add CPU latency QoS API wrappers Introduce (temporary) wrappers around pm_qos_request(), pm_qos_request_active() and pm_qos_add/update/remove_request() to provide replacements for them with function signatures that will be used in the final CPU latency QoS API, so that the users of it can be switched over to the new arrangement one by one before the API is finally set. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 3c4bee29ecda..63d39e66f95d 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -152,6 +152,33 @@ void pm_qos_remove_request(struct pm_qos_request *req); s32 pm_qos_request(int pm_qos_class); int pm_qos_request_active(struct pm_qos_request *req); +static inline void cpu_latency_qos_add_request(struct pm_qos_request *req, + s32 value) +{ + pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, value); +} + +static inline void cpu_latency_qos_update_request(struct pm_qos_request *req, + s32 new_value) +{ + pm_qos_update_request(req, new_value); +} + +static inline void cpu_latency_qos_remove_request(struct pm_qos_request *req) +{ + pm_qos_remove_request(req); +} + +static inline bool cpu_latency_qos_request_active(struct pm_qos_request *req) +{ + return pm_qos_request_active(req); +} + +static inline s32 cpu_latency_qos_limit(void) +{ + return pm_qos_request(PM_QOS_CPU_DMA_LATENCY); +} + #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask); From f60ccc3558dd83c4db68736625642d54bf38a6db Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:08:42 +0100 Subject: [PATCH 0176/2231] cpuidle: Call cpu_latency_qos_limit() instead of pm_qos_request() Call cpu_latency_qos_limit() instead of pm_qos_request(), because the latter is going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/cpuidle/governor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c index e48271e117a3..29acaf48e575 100644 --- a/drivers/cpuidle/governor.c +++ b/drivers/cpuidle/governor.c @@ -109,9 +109,9 @@ int cpuidle_register_governor(struct cpuidle_governor *gov) */ s64 cpuidle_governor_latency_req(unsigned int cpu) { - int global_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); struct device *device = get_cpu_device(cpu); int device_req = dev_pm_qos_raw_resume_latency(device); + int global_req = cpu_latency_qos_limit(); if (device_req > global_req) device_req = global_req; From fe66a17ecd4912401afd61517eadae2e9e4ce0ae Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:10:00 +0100 Subject: [PATCH 0177/2231] x86: platform: iosf_mbi: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/update/remove_request() instead of pm_qos_add/update/remove_request(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Andy Shevchenko Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- arch/x86/platform/intel/iosf_mbi.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/x86/platform/intel/iosf_mbi.c b/arch/x86/platform/intel/iosf_mbi.c index 9e2444500428..526f70f27c1c 100644 --- a/arch/x86/platform/intel/iosf_mbi.c +++ b/arch/x86/platform/intel/iosf_mbi.c @@ -265,7 +265,7 @@ static void iosf_mbi_reset_semaphore(void) iosf_mbi_sem_address, 0, PUNIT_SEMAPHORE_BIT)) dev_err(&mbi_pdev->dev, "Error P-Unit semaphore reset failed\n"); - pm_qos_update_request(&iosf_mbi_pm_qos, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_update_request(&iosf_mbi_pm_qos, PM_QOS_DEFAULT_VALUE); blocking_notifier_call_chain(&iosf_mbi_pmic_bus_access_notifier, MBI_PMIC_BUS_ACCESS_END, NULL); @@ -301,8 +301,8 @@ static void iosf_mbi_reset_semaphore(void) * 4) When CPU cores enter C6 or C7 the P-Unit needs to talk to the PMIC * if this happens while the kernel itself is accessing the PMIC I2C bus * the SoC hangs. - * As the third step we call pm_qos_update_request() to disallow the CPU - * to enter C6 or C7. + * As the third step we call cpu_latency_qos_update_request() to disallow the + * CPU to enter C6 or C7. * * 5) The P-Unit has a PMIC bus semaphore which we can request to stop * autonomous P-Unit tasks from accessing the PMIC I2C bus while we hold it. @@ -338,7 +338,7 @@ int iosf_mbi_block_punit_i2c_access(void) * requires the P-Unit to talk to the PMIC and if this happens while * we're holding the semaphore, the SoC hangs. */ - pm_qos_update_request(&iosf_mbi_pm_qos, 0); + cpu_latency_qos_update_request(&iosf_mbi_pm_qos, 0); /* host driver writes to side band semaphore register */ ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, @@ -547,8 +547,7 @@ static int __init iosf_mbi_init(void) { iosf_debugfs_init(); - pm_qos_add_request(&iosf_mbi_pm_qos, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_add_request(&iosf_mbi_pm_qos, PM_QOS_DEFAULT_VALUE); return pci_register_driver(&iosf_mbi_pci_driver); } @@ -561,7 +560,7 @@ static void __exit iosf_mbi_exit(void) pci_dev_put(mbi_pdev); mbi_pdev = NULL; - pm_qos_remove_request(&iosf_mbi_pm_qos); + cpu_latency_qos_remove_request(&iosf_mbi_pm_qos); } module_init(iosf_mbi_init); From ca065bf12771078571dec758300631624edc19a2 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 13 Feb 2020 12:13:36 +0100 Subject: [PATCH 0178/2231] USB: apple-mfi-fastcharge: fix endianess issue in probe The product ID is little endian and needs to be converted. Reported-by: kbuild test robot Signed-off-by: Oliver Neukum Reviewed-by: Bastien Nocera Link: https://lore.kernel.org/r/20200213111336.32392-1-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/apple-mfi-fastcharge.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index f1c4461a9a3c..b403094a6b3a 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -167,11 +167,11 @@ static int mfi_fc_probe(struct usb_device *udev) { struct power_supply_config battery_cfg = {}; struct mfi_device *mfi = NULL; - int err; + int err, idProduct; + idProduct = le16_to_cpu(udev->descriptor.idProduct); /* See comment above mfi_fc_id_table[] */ - if (udev->descriptor.idProduct < 0x1200 || - udev->descriptor.idProduct > 0x12ff) { + if (idProduct < 0x1200 || idProduct > 0x12ff) { return -ENODEV; } From 020bd6c48ebd864d42b5b551a87a323e443918a6 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 13 Feb 2020 12:16:05 +0800 Subject: [PATCH 0179/2231] spi: spidev_test: Remove break after exit statement When call print_usage() in parse_opts(), it will exit directly. Since break is not useful after exit statement, remove it. Signed-off-by: Tiezhu Yang Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/1581567368-8055-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Mark Brown --- tools/spi/spidev_test.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index 3559e7646256..113b1e1d62ca 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -283,7 +283,6 @@ static void parse_opts(int argc, char *argv[]) break; default: print_usage(argv[0]); - break; } } if (mode & SPI_LOOP) { From 1f3c36328a487059beebd1f7be042e3b7abf7d34 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 13 Feb 2020 12:16:06 +0800 Subject: [PATCH 0180/2231] spi: spidev_test: Check input_tx and input_file first after parse options It is better to check input_tx and input_file first after parse options. Otherwise, it will do some useless operations when both -p and --input are selected. Signed-off-by: Tiezhu Yang Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/1581567368-8055-2-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Mark Brown --- tools/spi/spidev_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index 113b1e1d62ca..5866178cdcc9 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -404,6 +404,9 @@ int main(int argc, char *argv[]) parse_opts(argc, argv); + if (input_tx && input_file) + pabort("only one of -p and --input may be selected"); + fd = open(device, O_RDWR); if (fd < 0) pabort("can't open device"); @@ -445,9 +448,6 @@ int main(int argc, char *argv[]) printf("bits per word: %d\n", bits); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); - if (input_tx && input_file) - pabort("only one of -p and --input may be selected"); - if (input_tx) transfer_escaped_string(fd, input_tx); else if (input_file) From 470a072e12200576788dc622d58894609feae2d7 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 13 Feb 2020 12:16:07 +0800 Subject: [PATCH 0181/2231] spi: spidev_test: Use perror() only if errno is not 0 It is better to use perror() only if errno is not 0, it should use printf() when errno is 0, otherwise there exists redudant ": Success". E.g. without this patch: $ ./spidev_test -p 1234 --input test.bin only one of -p and --input may be selected: Success Aborted (core dumped) With this patch: $ ./spidev_test -p 1234 --input test.bin only one of -p and --input may be selected Aborted (core dumped) Signed-off-by: Tiezhu Yang Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/1581567368-8055-3-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Mark Brown --- tools/spi/spidev_test.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index 5866178cdcc9..27967dd90f8f 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,11 @@ static void pabort(const char *s) { - perror(s); + if (errno != 0) + perror(s); + else + printf("%s\n", s); + abort(); } From aea7afd9079f2e43b05790241d748ff0537ec917 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 13 Feb 2020 12:16:08 +0800 Subject: [PATCH 0182/2231] spi: spidev_test: Remove the whole "include" directory when make clean In the current code, it only removes "include/linux/spi/spidev.h" file when make clean and there still exists useless "include/linux/spi/" directory, just remove it. Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/1581567368-8055-4-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Mark Brown --- tools/spi/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/spi/Makefile b/tools/spi/Makefile index 5c342e655e55..2249a1546cc1 100644 --- a/tools/spi/Makefile +++ b/tools/spi/Makefile @@ -51,7 +51,7 @@ $(OUTPUT)spidev_fdx: $(SPIDEV_FDX_IN) clean: rm -f $(ALL_PROGRAMS) - rm -f $(OUTPUT)include/linux/spi/spidev.h + rm -rf $(OUTPUT)include/ find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete install: $(ALL_PROGRAMS) From 34e75cf4beb1a88a61b7c76b5fdc99c43cff8594 Mon Sep 17 00:00:00 2001 From: "Daniel W. S. Almeida" Date: Wed, 29 Jan 2020 01:49:13 -0300 Subject: [PATCH 0183/2231] Documentation: nfs: convert pnfs.txt to ReST Convert pnfs.txt to ReST. Content remains mostly unchanged. Signed-off-by: Daniel W. S. Almeida Link: https://lore.kernel.org/r/20200129044917.566906-2-dwlsalmeida@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/filesystems/index.rst | 1 + Documentation/filesystems/nfs/index.rst | 9 +++++++ .../filesystems/nfs/{pnfs.txt => pnfs.rst} | 25 +++++++++++-------- 3 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 Documentation/filesystems/nfs/index.rst rename Documentation/filesystems/nfs/{pnfs.txt => pnfs.rst} (87%) diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst index 386eaad008b2..45d791905e91 100644 --- a/Documentation/filesystems/index.rst +++ b/Documentation/filesystems/index.rst @@ -51,3 +51,4 @@ Documentation for filesystem implementations. overlayfs virtiofs vfat + nfs/index diff --git a/Documentation/filesystems/nfs/index.rst b/Documentation/filesystems/nfs/index.rst new file mode 100644 index 000000000000..d19ba592779a --- /dev/null +++ b/Documentation/filesystems/nfs/index.rst @@ -0,0 +1,9 @@ +=============================== +NFS +=============================== + + +.. toctree:: + :maxdepth: 1 + + pnfs diff --git a/Documentation/filesystems/nfs/pnfs.txt b/Documentation/filesystems/nfs/pnfs.rst similarity index 87% rename from Documentation/filesystems/nfs/pnfs.txt rename to Documentation/filesystems/nfs/pnfs.rst index 80dc0bdc302a..7c470ecdc3a9 100644 --- a/Documentation/filesystems/nfs/pnfs.txt +++ b/Documentation/filesystems/nfs/pnfs.rst @@ -1,15 +1,17 @@ -Reference counting in pnfs: +========================== +Reference counting in pnfs ========================== The are several inter-related caches. We have layouts which can reference multiple devices, each of which can reference multiple data servers. Each data server can be referenced by multiple devices. Each device -can be referenced by multiple layouts. To keep all of this straight, +can be referenced by multiple layouts. To keep all of this straight, we need to reference count. struct pnfs_layout_hdr ----------------------- +====================== + The on-the-wire command LAYOUTGET corresponds to struct pnfs_layout_segment, usually referred to by the variable name lseg. Each nfs_inode may hold a pointer to a cache of these layout @@ -25,7 +27,8 @@ the reference count, as the layout is kept around by the lseg that keeps it in the list. deviceid_cache --------------- +============== + lsegs reference device ids, which are resolved per nfs_client and layout driver type. The device ids are held in a RCU cache (struct nfs4_deviceid_cache). The cache itself is referenced across each @@ -38,24 +41,26 @@ justification, but seems reasonable given that we can have multiple deviceid's per filesystem, and multiple filesystems per nfs_client. The hash code is copied from the nfsd code base. A discussion of -hashing and variations of this algorithm can be found at: -http://groups.google.com/group/comp.lang.c/browse_thread/thread/9522965e2b8d3809 +hashing and variations of this algorithm can be found `here. +`_ data server cache ------------------ +================= + file driver devices refer to data servers, which are kept in a module level cache. Its reference is held over the lifetime of the deviceid pointing to it. lseg ----- +==== + lseg maintains an extra reference corresponding to the NFS_LSEG_VALID bit which holds it in the pnfs_layout_hdr's list. When the final lseg is removed from the pnfs_layout_hdr's list, the NFS_LAYOUT_DESTROYED bit is set, preventing any new lsegs from being added. layout drivers --------------- +============== PNFS utilizes what is called layout drivers. The STD defines 4 basic layout types: "files", "objects", "blocks", and "flexfiles". For each @@ -68,6 +73,6 @@ Blocks-layout-driver code is in: fs/nfs/blocklayout/.. directory Flexfiles-layout-driver code is in: fs/nfs/flexfilelayout/.. directory blocks-layout setup -------------------- +=================== TODO: Document the setup needs of the blocks layout driver From f0bf8a988b26e75cc6fc28a44a745cb354a2b5a6 Mon Sep 17 00:00:00 2001 From: "Daniel W. S. Almeida" Date: Wed, 29 Jan 2020 01:49:14 -0300 Subject: [PATCH 0184/2231] Documentation: nfs: rpc-cache: convert to ReST Convert rpc-cache.txt to ReST. Changes aim to improve presentation but the content itself remains mostly the same. Signed-off-by: Daniel W. S. Almeida Link: https://lore.kernel.org/r/20200129044917.566906-3-dwlsalmeida@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/filesystems/nfs/index.rst | 1 + .../nfs/{rpc-cache.txt => rpc-cache.rst} | 132 ++++++++++-------- 2 files changed, 76 insertions(+), 57 deletions(-) rename Documentation/filesystems/nfs/{rpc-cache.txt => rpc-cache.rst} (66%) diff --git a/Documentation/filesystems/nfs/index.rst b/Documentation/filesystems/nfs/index.rst index d19ba592779a..52f4956e7770 100644 --- a/Documentation/filesystems/nfs/index.rst +++ b/Documentation/filesystems/nfs/index.rst @@ -7,3 +7,4 @@ NFS :maxdepth: 1 pnfs + rpc-cache diff --git a/Documentation/filesystems/nfs/rpc-cache.txt b/Documentation/filesystems/nfs/rpc-cache.rst similarity index 66% rename from Documentation/filesystems/nfs/rpc-cache.txt rename to Documentation/filesystems/nfs/rpc-cache.rst index c4dac829db0f..bb164eea969b 100644 --- a/Documentation/filesystems/nfs/rpc-cache.txt +++ b/Documentation/filesystems/nfs/rpc-cache.rst @@ -1,9 +1,14 @@ - This document gives a brief introduction to the caching +========= +RPC Cache +========= + +This document gives a brief introduction to the caching mechanisms in the sunrpc layer that is used, in particular, for NFS authentication. -CACHES +Caches ====== + The caching replaces the old exports table and allows for a wide variety of values to be caches. @@ -12,6 +17,7 @@ quite possibly very different in content and use. There is a corpus of common code for managing these caches. Examples of caches that are likely to be needed are: + - mapping from IP address to client name - mapping from client name and filesystem to export options - mapping from UID to list of GIDs, to work around NFS's limitation @@ -21,6 +27,7 @@ Examples of caches that are likely to be needed are: - mapping from network identify to public key for crypto authentication. The common code handles such things as: + - general cache lookup with correct locking - supporting 'NEGATIVE' as well as positive entries - allowing an EXPIRED time on cache items, and removing @@ -35,60 +42,66 @@ The common code handles such things as: Creating a Cache ---------------- -1/ A cache needs a datum to store. This is in the form of a - structure definition that must contain a - struct cache_head +- A cache needs a datum to store. This is in the form of a + structure definition that must contain a struct cache_head as an element, usually the first. It will also contain a key and some content. Each cache element is reference counted and contains expiry and update times for use in cache management. -2/ A cache needs a "cache_detail" structure that +- A cache needs a "cache_detail" structure that describes the cache. This stores the hash table, some parameters for cache management, and some operations detailing how to work with particular cache items. - The operations requires are: - struct cache_head *alloc(void) - This simply allocates appropriate memory and returns - a pointer to the cache_detail embedded within the - structure - void cache_put(struct kref *) - This is called when the last reference to an item is - dropped. The pointer passed is to the 'ref' field - in the cache_head. cache_put should release any - references create by 'cache_init' and, if CACHE_VALID - is set, any references created by cache_update. - It should then release the memory allocated by - 'alloc'. - int match(struct cache_head *orig, struct cache_head *new) - test if the keys in the two structures match. Return - 1 if they do, 0 if they don't. - void init(struct cache_head *orig, struct cache_head *new) - Set the 'key' fields in 'new' from 'orig'. This may - include taking references to shared objects. - void update(struct cache_head *orig, struct cache_head *new) - Set the 'content' fileds in 'new' from 'orig'. - int cache_show(struct seq_file *m, struct cache_detail *cd, - struct cache_head *h) - Optional. Used to provide a /proc file that lists the - contents of a cache. This should show one item, - usually on just one line. - int cache_request(struct cache_detail *cd, struct cache_head *h, - char **bpp, int *blen) - Format a request to be send to user-space for an item - to be instantiated. *bpp is a buffer of size *blen. - bpp should be moved forward over the encoded message, - and *blen should be reduced to show how much free - space remains. Return 0 on success or <0 if not - enough room or other problem. - int cache_parse(struct cache_detail *cd, char *buf, int len) - A message from user space has arrived to fill out a - cache entry. It is in 'buf' of length 'len'. - cache_parse should parse this, find the item in the - cache with sunrpc_cache_lookup_rcu, and update the item - with sunrpc_cache_update. + + The operations are: + + struct cache_head \*alloc(void) + This simply allocates appropriate memory and returns + a pointer to the cache_detail embedded within the + structure + + void cache_put(struct kref \*) + This is called when the last reference to an item is + dropped. The pointer passed is to the 'ref' field + in the cache_head. cache_put should release any + references create by 'cache_init' and, if CACHE_VALID + is set, any references created by cache_update. + It should then release the memory allocated by + 'alloc'. + + int match(struct cache_head \*orig, struct cache_head \*new) + test if the keys in the two structures match. Return + 1 if they do, 0 if they don't. + + void init(struct cache_head \*orig, struct cache_head \*new) + Set the 'key' fields in 'new' from 'orig'. This may + include taking references to shared objects. + + void update(struct cache_head \*orig, struct cache_head \*new) + Set the 'content' fileds in 'new' from 'orig'. + + int cache_show(struct seq_file \*m, struct cache_detail \*cd, struct cache_head \*h) + Optional. Used to provide a /proc file that lists the + contents of a cache. This should show one item, + usually on just one line. + + int cache_request(struct cache_detail \*cd, struct cache_head \*h, char \*\*bpp, int \*blen) + Format a request to be send to user-space for an item + to be instantiated. \*bpp is a buffer of size \*blen. + bpp should be moved forward over the encoded message, + and \*blen should be reduced to show how much free + space remains. Return 0 on success or <0 if not + enough room or other problem. + + int cache_parse(struct cache_detail \*cd, char \*buf, int len) + A message from user space has arrived to fill out a + cache entry. It is in 'buf' of length 'len'. + cache_parse should parse this, find the item in the + cache with sunrpc_cache_lookup_rcu, and update the item + with sunrpc_cache_update. -3/ A cache needs to be registered using cache_register(). This +- A cache needs to be registered using cache_register(). This includes it on a list of caches that will be regularly cleaned to discard old data. @@ -107,7 +120,7 @@ cache_check will return -ENOENT in the entry is negative or if an up call is needed but not possible, -EAGAIN if an upcall is pending, or 0 if the data is valid; -cache_check can be passed a "struct cache_req *". This structure is +cache_check can be passed a "struct cache_req\*". This structure is typically embedded in the actual request and can be used to create a deferred copy of the request (struct cache_deferred_req). This is done when the found cache item is not uptodate, but the is reason to @@ -139,9 +152,11 @@ The 'channel' works a bit like a datagram socket. Each 'write' is passed as a whole to the cache for parsing and interpretation. Each cache can treat the write requests differently, but it is expected that a message written will contain: + - a key - an expiry time - a content. + with the intention that an item in the cache with the give key should be create or updated to have the given content, and the expiry time should be set on that item. @@ -156,7 +171,8 @@ If there are no more requests to return, read will return EOF, but a select or poll for read will block waiting for another request to be added. -Thus a user-space helper is likely to: +Thus a user-space helper is likely to:: + open the channel. select for readable read a request @@ -175,12 +191,13 @@ Each cache should also define a "cache_request" method which takes a cache item and encodes a request into the buffer provided. -Note: If a cache has no active readers on the channel, and has had not -active readers for more than 60 seconds, further requests will not be -added to the channel but instead all lookups that do not find a valid -entry will fail. This is partly for backward compatibility: The -previous nfs exports table was deemed to be authoritative and a -failed lookup meant a definite 'no'. +.. note:: + If a cache has no active readers on the channel, and has had not + active readers for more than 60 seconds, further requests will not be + added to the channel but instead all lookups that do not find a valid + entry will fail. This is partly for backward compatibility: The + previous nfs exports table was deemed to be authoritative and a + failed lookup meant a definite 'no'. request/response format ----------------------- @@ -193,10 +210,11 @@ with precisely one newline character which should be at the end. Fields within the record should be separated by spaces, normally one. If spaces, newlines, or nul characters are needed in a field they much be quoted. two mechanisms are available: -1/ If a field begins '\x' then it must contain an even number of + +- If a field begins '\x' then it must contain an even number of hex digits, and pairs of these digits provide the bytes in the field. -2/ otherwise a \ in the field must be followed by 3 octal digits +- otherwise a \ in the field must be followed by 3 octal digits which give the code for a byte. Other characters are treated as them selves. At the very least, space, newline, nul, and '\' must be quoted in this way. From 250baf06aacf4eafb5641c86c91f2b1df4cf7d86 Mon Sep 17 00:00:00 2001 From: "Daniel W. S. Almeida" Date: Wed, 29 Jan 2020 01:49:15 -0300 Subject: [PATCH 0185/2231] Documentation: nfs: rpc-server-gss: convert to ReST Convert rpc-server-gss.txt to ReST. Content remains mostly unchanged. Signed-off-by: Daniel W. S. Almeida Link: https://lore.kernel.org/r/20200129044917.566906-4-dwlsalmeida@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/filesystems/nfs/index.rst | 1 + ...{rpc-server-gss.txt => rpc-server-gss.rst} | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) rename Documentation/filesystems/nfs/{rpc-server-gss.txt => rpc-server-gss.rst} (92%) diff --git a/Documentation/filesystems/nfs/index.rst b/Documentation/filesystems/nfs/index.rst index 52f4956e7770..9d5365cbe2c3 100644 --- a/Documentation/filesystems/nfs/index.rst +++ b/Documentation/filesystems/nfs/index.rst @@ -8,3 +8,4 @@ NFS pnfs rpc-cache + rpc-server-gss diff --git a/Documentation/filesystems/nfs/rpc-server-gss.txt b/Documentation/filesystems/nfs/rpc-server-gss.rst similarity index 92% rename from Documentation/filesystems/nfs/rpc-server-gss.txt rename to Documentation/filesystems/nfs/rpc-server-gss.rst index 310bbbaf9080..812754576845 100644 --- a/Documentation/filesystems/nfs/rpc-server-gss.txt +++ b/Documentation/filesystems/nfs/rpc-server-gss.rst @@ -1,4 +1,4 @@ - +========================================= rpcsec_gss support for kernel RPC servers ========================================= @@ -9,14 +9,17 @@ NFSv4.1 and higher don't require the client to act as a server for the purposes of authentication.) RPCGSS is specified in a few IETF documents: + - RFC2203 v1: http://tools.ietf.org/rfc/rfc2203.txt - RFC5403 v2: http://tools.ietf.org/rfc/rfc5403.txt + and there is a 3rd version being proposed: + - http://tools.ietf.org/id/draft-williams-rpcsecgssv3.txt (At draft n. 02 at the time of writing) Background ----------- +========== The RPCGSS Authentication method describes a way to perform GSSAPI Authentication for NFS. Although GSSAPI is itself completely mechanism @@ -29,6 +32,7 @@ depends on GSSAPI extensions that are KRB5 specific. GSSAPI is a complex library, and implementing it completely in kernel is unwarranted. However GSSAPI operations are fundementally separable in 2 parts: + - initial context establishment - integrity/privacy protection (signing and encrypting of individual packets) @@ -41,7 +45,7 @@ kernel, but leave the initial context establishment to userspace. We need upcalls to request userspace to perform context establishment. NFS Server Legacy Upcall Mechanism ----------------------------------- +================================== The classic upcall mechanism uses a custom text based upcall mechanism to talk to a custom daemon called rpc.svcgssd that is provide by the @@ -62,21 +66,20 @@ groups) due to limitation on the size of the buffer that can be send back to the kernel (4KiB). NFS Server New RPC Upcall Mechanism ------------------------------------ +=================================== The newer upcall mechanism uses RPC over a unix socket to a daemon called gss-proxy, implemented by a userspace program called Gssproxy. -The gss_proxy RPC protocol is currently documented here: - - https://fedorahosted.org/gss-proxy/wiki/ProtocolDocumentation +The gss_proxy RPC protocol is currently documented `here +`_. This upcall mechanism uses the kernel rpc client and connects to the gssproxy userspace program over a regular unix socket. The gssproxy protocol does not suffer from the size limitations of the legacy protocol. Negotiating Upcall Mechanisms ------------------------------ +============================= To provide backward compatibility, the kernel defaults to using the legacy mechanism. To switch to the new mechanism, gss-proxy must bind From 04f81fb08d067f79c59fe132929a9c81eb9cb74b Mon Sep 17 00:00:00 2001 From: "Daniel W. S. Almeida" Date: Wed, 29 Jan 2020 01:49:16 -0300 Subject: [PATCH 0186/2231] Documentation: nfs: nfs41-server: convert to ReST Convert nfs41-server.txt to ReST. ASCII tables were converted to ReST grid table format. Signed-off-by: Daniel W. S. Almeida Link: https://lore.kernel.org/r/20200129044917.566906-5-dwlsalmeida@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/filesystems/nfs/index.rst | 1 + .../filesystems/nfs/nfs41-server.rst | 256 ++++++++++++++++++ .../filesystems/nfs/nfs41-server.txt | 173 ------------ 3 files changed, 257 insertions(+), 173 deletions(-) create mode 100644 Documentation/filesystems/nfs/nfs41-server.rst delete mode 100644 Documentation/filesystems/nfs/nfs41-server.txt diff --git a/Documentation/filesystems/nfs/index.rst b/Documentation/filesystems/nfs/index.rst index 9d5365cbe2c3..a0a678af921b 100644 --- a/Documentation/filesystems/nfs/index.rst +++ b/Documentation/filesystems/nfs/index.rst @@ -9,3 +9,4 @@ NFS pnfs rpc-cache rpc-server-gss + nfs41-server diff --git a/Documentation/filesystems/nfs/nfs41-server.rst b/Documentation/filesystems/nfs/nfs41-server.rst new file mode 100644 index 000000000000..16b5f02f81c3 --- /dev/null +++ b/Documentation/filesystems/nfs/nfs41-server.rst @@ -0,0 +1,256 @@ +============================= +NFSv4.1 Server Implementation +============================= + +Server support for minorversion 1 can be controlled using the +/proc/fs/nfsd/versions control file. The string output returned +by reading this file will contain either "+4.1" or "-4.1" +correspondingly. + +Currently, server support for minorversion 1 is enabled by default. +It can be disabled at run time by writing the string "-4.1" to +the /proc/fs/nfsd/versions control file. Note that to write this +control file, the nfsd service must be taken down. You can use rpc.nfsd +for this; see rpc.nfsd(8). + +(Warning: older servers will interpret "+4.1" and "-4.1" as "+4" and +"-4", respectively. Therefore, code meant to work on both new and old +kernels must turn 4.1 on or off *before* turning support for version 4 +on or off; rpc.nfsd does this correctly.) + +The NFSv4 minorversion 1 (NFSv4.1) implementation in nfsd is based +on RFC 5661. + +From the many new features in NFSv4.1 the current implementation +focuses on the mandatory-to-implement NFSv4.1 Sessions, providing +"exactly once" semantics and better control and throttling of the +resources allocated for each client. + +The table below, taken from the NFSv4.1 document, lists +the operations that are mandatory to implement (REQ), optional +(OPT), and NFSv4.0 operations that are required not to implement (MNI) +in minor version 1. The first column indicates the operations that +are not supported yet by the linux server implementation. + +The OPTIONAL features identified and their abbreviations are as follows: + +- **pNFS** Parallel NFS +- **FDELG** File Delegations +- **DDELG** Directory Delegations + +The following abbreviations indicate the linux server implementation status. + +- **I** Implemented NFSv4.1 operations. +- **NS** Not Supported. +- **NS\*** Unimplemented optional feature. + +Operations +========== + ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| Implementation status | Operation | REQ,REC, OPT or NMI | Feature (REQ, REC or OPT) | Definition | ++=======================+======================+=====================+===========================+================+ +| | ACCESS | REQ | | Section 18.1 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | BACKCHANNEL_CTL | REQ | | Section 18.33 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | BIND_CONN_TO_SESSION | REQ | | Section 18.34 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | CLOSE | REQ | | Section 18.2 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | COMMIT | REQ | | Section 18.3 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | CREATE | REQ | | Section 18.4 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | CREATE_SESSION | REQ | | Section 18.36 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| NS* | DELEGPURGE | OPT | FDELG (REQ) | Section 18.5 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | DELEGRETURN | OPT | FDELG, | Section 18.6 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | | | DDELG, pNFS | | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | | | (REQ) | | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | DESTROY_CLIENTID | REQ | | Section 18.50 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | DESTROY_SESSION | REQ | | Section 18.37 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | EXCHANGE_ID | REQ | | Section 18.35 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | FREE_STATEID | REQ | | Section 18.38 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | GETATTR | REQ | | Section 18.7 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | GETDEVICEINFO | OPT | pNFS (REQ) | Section 18.40 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| NS* | GETDEVICELIST | OPT | pNFS (OPT) | Section 18.41 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | GETFH | REQ | | Section 18.8 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| NS* | GET_DIR_DELEGATION | OPT | DDELG (REQ) | Section 18.39 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | LAYOUTCOMMIT | OPT | pNFS (REQ) | Section 18.42 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | LAYOUTGET | OPT | pNFS (REQ) | Section 18.43 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | LAYOUTRETURN | OPT | pNFS (REQ) | Section 18.44 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | LINK | OPT | | Section 18.9 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | LOCK | REQ | | Section 18.10 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | LOCKT | REQ | | Section 18.11 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | LOCKU | REQ | | Section 18.12 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | LOOKUP | REQ | | Section 18.13 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | LOOKUPP | REQ | | Section 18.14 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | NVERIFY | REQ | | Section 18.15 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | OPEN | REQ | | Section 18.16 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| NS* | OPENATTR | OPT | | Section 18.17 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | OPEN_CONFIRM | MNI | | N/A | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | OPEN_DOWNGRADE | REQ | | Section 18.18 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | PUTFH | REQ | | Section 18.19 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | PUTPUBFH | REQ | | Section 18.20 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | PUTROOTFH | REQ | | Section 18.21 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | READ | REQ | | Section 18.22 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | READDIR | REQ | | Section 18.23 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | READLINK | OPT | | Section 18.24 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | RECLAIM_COMPLETE | REQ | | Section 18.51 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | RELEASE_LOCKOWNER | MNI | | N/A | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | REMOVE | REQ | | Section 18.25 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | RENAME | REQ | | Section 18.26 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | RENEW | MNI | | N/A | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | RESTOREFH | REQ | | Section 18.27 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | SAVEFH | REQ | | Section 18.28 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | SECINFO | REQ | | Section 18.29 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | SECINFO_NO_NAME | REC | pNFS files | Section 18.45, | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | | | layout (REQ) | Section 13.12 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | SEQUENCE | REQ | | Section 18.46 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | SETATTR | REQ | | Section 18.30 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | SETCLIENTID | MNI | | N/A | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | SETCLIENTID_CONFIRM | MNI | | N/A | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| NS | SET_SSV | REQ | | Section 18.47 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| I | TEST_STATEID | REQ | | Section 18.48 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | VERIFY | REQ | | Section 18.31 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| NS* | WANT_DELEGATION | OPT | FDELG (OPT) | Section 18.49 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ +| | WRITE | REQ | | Section 18.32 | ++-----------------------+----------------------+---------------------+---------------------------+----------------+ + + +Callback Operations +=================== ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| Implementation status | Operation | REQ,REC, OPT or NMI | Feature (REQ, REC or OPT) | Definition | ++=======================+=========================+=====================+===========================+===============+ +| | CB_GETATTR | OPT | FDELG (REQ) | Section 20.1 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| I | CB_LAYOUTRECALL | OPT | pNFS (REQ) | Section 20.3 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| NS* | CB_NOTIFY | OPT | DDELG (REQ) | Section 20.4 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| NS* | CB_NOTIFY_DEVICEID | OPT | pNFS (OPT) | Section 20.12 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| NS* | CB_NOTIFY_LOCK | OPT | | Section 20.11 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| NS* | CB_PUSH_DELEG | OPT | FDELG (OPT) | Section 20.5 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | CB_RECALL | OPT | FDELG, | Section 20.2 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | DDELG, pNFS | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | (REQ) | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| NS* | CB_RECALL_ANY | OPT | FDELG, | Section 20.6 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | DDELG, pNFS | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | (REQ) | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| NS | CB_RECALL_SLOT | REQ | | Section 20.8 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| NS* | CB_RECALLABLE_OBJ_AVAIL | OPT | DDELG, pNFS | Section 20.7 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | (REQ) | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| I | CB_SEQUENCE | OPT | FDELG, | Section 20.9 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | DDELG, pNFS | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | (REQ) | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| NS* | CB_WANTS_CANCELLED | OPT | FDELG, | Section 20.10 | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | DDELG, pNFS | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ +| | | | (REQ) | | ++-----------------------+-------------------------+---------------------+---------------------------+---------------+ + + +Implementation notes: +===================== + +SSV: + The spec claims this is mandatory, but we don't actually know of any + implementations, so we're ignoring it for now. The server returns + NFS4ERR_ENCR_ALG_UNSUPP on EXCHANGE_ID, which should be future-proof. + +GSS on the backchannel: + Again, theoretically required but not widely implemented (in + particular, the current Linux client doesn't request it). We return + NFS4ERR_ENCR_ALG_UNSUPP on CREATE_SESSION. + +DELEGPURGE: + mandatory only for servers that support CLAIM_DELEGATE_PREV and/or + CLAIM_DELEG_PREV_FH (which allows clients to keep delegations that + persist across client reboots). Thus we need not implement this for + now. + +EXCHANGE_ID: + implementation ids are ignored + +CREATE_SESSION: + backchannel attributes are ignored + +SEQUENCE: + no support for dynamic slot table renegotiation (optional) + +Nonstandard compound limitations: + No support for a sessions fore channel RPC compound that requires both a + ca_maxrequestsize request and a ca_maxresponsesize reply, so we may + fail to live up to the promise we made in CREATE_SESSION fore channel + negotiation. + +See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues. diff --git a/Documentation/filesystems/nfs/nfs41-server.txt b/Documentation/filesystems/nfs/nfs41-server.txt deleted file mode 100644 index 682a59fabe3f..000000000000 --- a/Documentation/filesystems/nfs/nfs41-server.txt +++ /dev/null @@ -1,173 +0,0 @@ -NFSv4.1 Server Implementation - -Server support for minorversion 1 can be controlled using the -/proc/fs/nfsd/versions control file. The string output returned -by reading this file will contain either "+4.1" or "-4.1" -correspondingly. - -Currently, server support for minorversion 1 is enabled by default. -It can be disabled at run time by writing the string "-4.1" to -the /proc/fs/nfsd/versions control file. Note that to write this -control file, the nfsd service must be taken down. You can use rpc.nfsd -for this; see rpc.nfsd(8). - -(Warning: older servers will interpret "+4.1" and "-4.1" as "+4" and -"-4", respectively. Therefore, code meant to work on both new and old -kernels must turn 4.1 on or off *before* turning support for version 4 -on or off; rpc.nfsd does this correctly.) - -The NFSv4 minorversion 1 (NFSv4.1) implementation in nfsd is based -on RFC 5661. - -From the many new features in NFSv4.1 the current implementation -focuses on the mandatory-to-implement NFSv4.1 Sessions, providing -"exactly once" semantics and better control and throttling of the -resources allocated for each client. - -The table below, taken from the NFSv4.1 document, lists -the operations that are mandatory to implement (REQ), optional -(OPT), and NFSv4.0 operations that are required not to implement (MNI) -in minor version 1. The first column indicates the operations that -are not supported yet by the linux server implementation. - -The OPTIONAL features identified and their abbreviations are as follows: - pNFS Parallel NFS - FDELG File Delegations - DDELG Directory Delegations - -The following abbreviations indicate the linux server implementation status. - I Implemented NFSv4.1 operations. - NS Not Supported. - NS* Unimplemented optional feature. - -Operations - - +----------------------+------------+--------------+----------------+ - | Operation | REQ, REC, | Feature | Definition | - | | OPT, or | (REQ, REC, | | - | | MNI | or OPT) | | - +----------------------+------------+--------------+----------------+ - | ACCESS | REQ | | Section 18.1 | -I | BACKCHANNEL_CTL | REQ | | Section 18.33 | -I | BIND_CONN_TO_SESSION | REQ | | Section 18.34 | - | CLOSE | REQ | | Section 18.2 | - | COMMIT | REQ | | Section 18.3 | - | CREATE | REQ | | Section 18.4 | -I | CREATE_SESSION | REQ | | Section 18.36 | -NS*| DELEGPURGE | OPT | FDELG (REQ) | Section 18.5 | - | DELEGRETURN | OPT | FDELG, | Section 18.6 | - | | | DDELG, pNFS | | - | | | (REQ) | | -I | DESTROY_CLIENTID | REQ | | Section 18.50 | -I | DESTROY_SESSION | REQ | | Section 18.37 | -I | EXCHANGE_ID | REQ | | Section 18.35 | -I | FREE_STATEID | REQ | | Section 18.38 | - | GETATTR | REQ | | Section 18.7 | -I | GETDEVICEINFO | OPT | pNFS (REQ) | Section 18.40 | -NS*| GETDEVICELIST | OPT | pNFS (OPT) | Section 18.41 | - | GETFH | REQ | | Section 18.8 | -NS*| GET_DIR_DELEGATION | OPT | DDELG (REQ) | Section 18.39 | -I | LAYOUTCOMMIT | OPT | pNFS (REQ) | Section 18.42 | -I | LAYOUTGET | OPT | pNFS (REQ) | Section 18.43 | -I | LAYOUTRETURN | OPT | pNFS (REQ) | Section 18.44 | - | LINK | OPT | | Section 18.9 | - | LOCK | REQ | | Section 18.10 | - | LOCKT | REQ | | Section 18.11 | - | LOCKU | REQ | | Section 18.12 | - | LOOKUP | REQ | | Section 18.13 | - | LOOKUPP | REQ | | Section 18.14 | - | NVERIFY | REQ | | Section 18.15 | - | OPEN | REQ | | Section 18.16 | -NS*| OPENATTR | OPT | | Section 18.17 | - | OPEN_CONFIRM | MNI | | N/A | - | OPEN_DOWNGRADE | REQ | | Section 18.18 | - | PUTFH | REQ | | Section 18.19 | - | PUTPUBFH | REQ | | Section 18.20 | - | PUTROOTFH | REQ | | Section 18.21 | - | READ | REQ | | Section 18.22 | - | READDIR | REQ | | Section 18.23 | - | READLINK | OPT | | Section 18.24 | - | RECLAIM_COMPLETE | REQ | | Section 18.51 | - | RELEASE_LOCKOWNER | MNI | | N/A | - | REMOVE | REQ | | Section 18.25 | - | RENAME | REQ | | Section 18.26 | - | RENEW | MNI | | N/A | - | RESTOREFH | REQ | | Section 18.27 | - | SAVEFH | REQ | | Section 18.28 | - | SECINFO | REQ | | Section 18.29 | -I | SECINFO_NO_NAME | REC | pNFS files | Section 18.45, | - | | | layout (REQ) | Section 13.12 | -I | SEQUENCE | REQ | | Section 18.46 | - | SETATTR | REQ | | Section 18.30 | - | SETCLIENTID | MNI | | N/A | - | SETCLIENTID_CONFIRM | MNI | | N/A | -NS | SET_SSV | REQ | | Section 18.47 | -I | TEST_STATEID | REQ | | Section 18.48 | - | VERIFY | REQ | | Section 18.31 | -NS*| WANT_DELEGATION | OPT | FDELG (OPT) | Section 18.49 | - | WRITE | REQ | | Section 18.32 | - -Callback Operations - - +-------------------------+-----------+-------------+---------------+ - | Operation | REQ, REC, | Feature | Definition | - | | OPT, or | (REQ, REC, | | - | | MNI | or OPT) | | - +-------------------------+-----------+-------------+---------------+ - | CB_GETATTR | OPT | FDELG (REQ) | Section 20.1 | -I | CB_LAYOUTRECALL | OPT | pNFS (REQ) | Section 20.3 | -NS*| CB_NOTIFY | OPT | DDELG (REQ) | Section 20.4 | -NS*| CB_NOTIFY_DEVICEID | OPT | pNFS (OPT) | Section 20.12 | -NS*| CB_NOTIFY_LOCK | OPT | | Section 20.11 | -NS*| CB_PUSH_DELEG | OPT | FDELG (OPT) | Section 20.5 | - | CB_RECALL | OPT | FDELG, | Section 20.2 | - | | | DDELG, pNFS | | - | | | (REQ) | | -NS*| CB_RECALL_ANY | OPT | FDELG, | Section 20.6 | - | | | DDELG, pNFS | | - | | | (REQ) | | -NS | CB_RECALL_SLOT | REQ | | Section 20.8 | -NS*| CB_RECALLABLE_OBJ_AVAIL | OPT | DDELG, pNFS | Section 20.7 | - | | | (REQ) | | -I | CB_SEQUENCE | OPT | FDELG, | Section 20.9 | - | | | DDELG, pNFS | | - | | | (REQ) | | -NS*| CB_WANTS_CANCELLED | OPT | FDELG, | Section 20.10 | - | | | DDELG, pNFS | | - | | | (REQ) | | - +-------------------------+-----------+-------------+---------------+ - -Implementation notes: - -SSV: -* The spec claims this is mandatory, but we don't actually know of any - implementations, so we're ignoring it for now. The server returns - NFS4ERR_ENCR_ALG_UNSUPP on EXCHANGE_ID, which should be future-proof. - -GSS on the backchannel: -* Again, theoretically required but not widely implemented (in - particular, the current Linux client doesn't request it). We return - NFS4ERR_ENCR_ALG_UNSUPP on CREATE_SESSION. - -DELEGPURGE: -* mandatory only for servers that support CLAIM_DELEGATE_PREV and/or - CLAIM_DELEG_PREV_FH (which allows clients to keep delegations that - persist across client reboots). Thus we need not implement this for - now. - -EXCHANGE_ID: -* implementation ids are ignored - -CREATE_SESSION: -* backchannel attributes are ignored - -SEQUENCE: -* no support for dynamic slot table renegotiation (optional) - -Nonstandard compound limitations: -* No support for a sessions fore channel RPC compound that requires both a - ca_maxrequestsize request and a ca_maxresponsesize reply, so we may - fail to live up to the promise we made in CREATE_SESSION fore channel - negotiation. - -See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues. From cb63032b1233e03ac20fc2b60820a50d605b9bc0 Mon Sep 17 00:00:00 2001 From: "Daniel W. S. Almeida" Date: Wed, 29 Jan 2020 01:49:17 -0300 Subject: [PATCH 0187/2231] Documentation: nfs: knfsd-stats: convert to ReST Convert knfsd-stats.txt to ReST. Content remains mostly the same. Signed-off-by: Daniel W. S. Almeida Link: https://lore.kernel.org/r/20200129044917.566906-6-dwlsalmeida@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/filesystems/nfs/index.rst | 1 + .../nfs/{knfsd-stats.txt => knfsd-stats.rst} | 17 ++++++++--------- 2 files changed, 9 insertions(+), 9 deletions(-) rename Documentation/filesystems/nfs/{knfsd-stats.txt => knfsd-stats.rst} (95%) diff --git a/Documentation/filesystems/nfs/index.rst b/Documentation/filesystems/nfs/index.rst index a0a678af921b..65805624e39b 100644 --- a/Documentation/filesystems/nfs/index.rst +++ b/Documentation/filesystems/nfs/index.rst @@ -10,3 +10,4 @@ NFS rpc-cache rpc-server-gss nfs41-server + knfsd-stats diff --git a/Documentation/filesystems/nfs/knfsd-stats.txt b/Documentation/filesystems/nfs/knfsd-stats.rst similarity index 95% rename from Documentation/filesystems/nfs/knfsd-stats.txt rename to Documentation/filesystems/nfs/knfsd-stats.rst index 1a5d82180b84..80bcf13550de 100644 --- a/Documentation/filesystems/nfs/knfsd-stats.txt +++ b/Documentation/filesystems/nfs/knfsd-stats.rst @@ -1,7 +1,9 @@ - +============================ Kernel NFS Server Statistics ============================ +:Authors: Greg Banks - 26 Mar 2009 + This document describes the format and semantics of the statistics which the kernel NFS server makes available to userspace. These statistics are available in several text form pseudo files, each of @@ -18,7 +20,7 @@ by parsing routines. All other lines contain a sequence of fields separated by whitespace. /proc/fs/nfsd/pool_stats ------------------------- +======================== This file is available in kernels from 2.6.30 onwards, if the /proc/fs/nfsd filesystem is mounted (it almost always should be). @@ -109,15 +111,12 @@ this case), or the transport can be enqueued for later attention (sockets-enqueued counts this case), or the packet can be temporarily deferred because the transport is currently being used by an nfsd thread. This last case is not very interesting and is not explicitly -counted, but can be inferred from the other counters thus: +counted, but can be inferred from the other counters thus:: -packets-deferred = packets-arrived - ( sockets-enqueued + threads-woken ) + packets-deferred = packets-arrived - ( sockets-enqueued + threads-woken ) More ----- +==== + Descriptions of the other statistics file should go here. - - -Greg Banks -26 Mar 2009 From 56e6b3b0b381abd0484802828764d01552ff76ab Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Thu, 6 Feb 2020 19:10:31 +0800 Subject: [PATCH 0188/2231] Documentation: zram: fix the description about orig_data_size of mm_stat orig_data_size counted the same_pages by commit 51f9f82c855d ("zram: count same page write as page_stored"), so let's fix it. Signed-off-by: Yue Hu Link: https://lore.kernel.org/r/20200206111031.9524-1-zbestahu@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/blockdev/zram.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/Documentation/admin-guide/blockdev/zram.rst b/Documentation/admin-guide/blockdev/zram.rst index 27c77d853028..a6fd1f9b5faf 100644 --- a/Documentation/admin-guide/blockdev/zram.rst +++ b/Documentation/admin-guide/blockdev/zram.rst @@ -251,8 +251,6 @@ line of text and contains the following stats separated by whitespace: ================ ============================================================= orig_data_size uncompressed size of data stored in this disk. - This excludes same-element-filled pages (same_pages) since - no memory is allocated for them. Unit: bytes compr_data_size compressed size of data stored in this disk mem_used_total the amount of memory allocated for this disk. This From 895f2c20a88a343d12c387dab9d785ff665cb4ac Mon Sep 17 00:00:00 2001 From: "d.hatayama@fujitsu.com" Date: Thu, 13 Feb 2020 02:51:49 +0000 Subject: [PATCH 0189/2231] docs: admin-guide: Add description of %c corename format There is somehow no description of %c corename format specifier for /proc/sys/kernel/core_pattern. The %c corename format specifier is used by user-space application such as systemd-coredump, so it should be documented. To find where %c is handled in the kernel source code, look at function format_corename() in fs/coredump.c. Signed-off-by: HATAYAMA Daisuke Link: https://lore.kernel.org/r/TYAPR01MB4014714BB2ACE425BB6EC6B7951A0@TYAPR01MB4014.jpnprd01.prod.outlook.com Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index def074807cee..b08ba4e63291 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -213,6 +213,7 @@ core_pattern is used to specify a core dumpfile pattern name. %h hostname %e executable filename (may be shortened) %E executable path + %c maximum size of core file by resource limit RLIMIT_CORE % both are dropped * If the first character of the pattern is a '|', the kernel will treat From 3b82a112ce594889742164b242d8f213938a443f Mon Sep 17 00:00:00 2001 From: Wang Long Date: Fri, 7 Feb 2020 21:42:10 +0800 Subject: [PATCH 0190/2231] Documentation/ABI: move sysfs-kernel-uids to removed directory commit 7c9414385ebf ("sched: Remove USER_SCHED") deleted the USER_SCHED feature. so move the ABI doc to removed directory. Signed-off-by: Wang Long Link: https://lore.kernel.org/r/1581082930-30441-1-git-send-email-w@laoqinren.net Signed-off-by: Jonathan Corbet --- Documentation/ABI/{testing => removed}/sysfs-kernel-uids | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Documentation/ABI/{testing => removed}/sysfs-kernel-uids (91%) diff --git a/Documentation/ABI/testing/sysfs-kernel-uids b/Documentation/ABI/removed/sysfs-kernel-uids similarity index 91% rename from Documentation/ABI/testing/sysfs-kernel-uids rename to Documentation/ABI/removed/sysfs-kernel-uids index 4182b7061816..dc4463f190a7 100644 --- a/Documentation/ABI/testing/sysfs-kernel-uids +++ b/Documentation/ABI/removed/sysfs-kernel-uids @@ -1,5 +1,5 @@ What: /sys/kernel/uids//cpu_shares -Date: December 2007 +Date: December 2007, finally removed in kernel v2.6.34-rc1 Contact: Dhaval Giani Srivatsa Vaddagiri Description: From 473da2f0d80aa7240dd0a2be5015fdfd93543ca2 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sun, 9 Feb 2020 21:33:04 +0100 Subject: [PATCH 0191/2231] docs: userspace: ioctl-number: remove mc146818rtc conflict In 2.3.43pre2, the RTC ioctls definitions were actually moved from linux/mc146818rtc.h to linux/rtc.h Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20200209203304.66004-1-alexandre.belloni@bootlin.com Signed-off-by: Jonathan Corbet --- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 2e91370dc159..f759edafd938 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -266,7 +266,6 @@ Code Seq# Include File Comments 'o' 01-A1 `linux/dvb/*.h` DVB 'p' 00-0F linux/phantom.h conflict! (OpenHaptics needs this) 'p' 00-1F linux/rtc.h conflict! -'p' 00-3F linux/mc146818rtc.h conflict! 'p' 40-7F linux/nvram.h 'p' 80-9F linux/ppdev.h user-space parport From 2e5b1886e9bab6c29c5e5c3ce4e373bb9e9eaa8b Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 9 Feb 2020 19:53:17 -0800 Subject: [PATCH 0192/2231] Documentation: bootconfig: fix Sphinx block warning Fix Sphinx format warning: lnx-56-rc1/Documentation/admin-guide/bootconfig.rst:26: WARNING: Literal block expected; none found. Signed-off-by: Randy Dunlap Cc: Steven Rostedt Acked-by: Masami Hiramatsu Link: https://lore.kernel.org/r/07b3e31f-9b1e-1876-aa60-4436e4dd6da0@infradead.org Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/bootconfig.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/bootconfig.rst b/Documentation/admin-guide/bootconfig.rst index b342a6796392..e603ebb5bdda 100644 --- a/Documentation/admin-guide/bootconfig.rst +++ b/Documentation/admin-guide/bootconfig.rst @@ -23,7 +23,7 @@ of dot-connected-words, and key and value are connected by ``=``. The value has to be terminated by semi-colon (``;``) or newline (``\n``). For array value, array entries are separated by comma (``,``). :: -KEY[.WORD[...]] = VALUE[, VALUE2[...]][;] + KEY[.WORD[...]] = VALUE[, VALUE2[...]][;] Unlike the kernel command line syntax, spaces are OK around the comma and ``=``. From e2327678732d78c90dec024e0ba4c9b65ca9cdd2 Mon Sep 17 00:00:00 2001 From: Marcelo Diop-Gonzalez Date: Thu, 13 Feb 2020 14:40:01 -0500 Subject: [PATCH 0193/2231] staging: vc04_services: Fix wrong early return in next_service_by_instance() If kref_get_unless_zero() fails, we should keep looking for the next service, since the callers of this function expect that a NULL return value means there are no more. Signed-off-by: Marcelo Diop-Gonzalez Link: https://lore.kernel.org/r/20200213194001.130110-1-marcgonzalez@google.com Signed-off-by: Greg Kroah-Hartman --- .../vc04_services/interface/vchiq_arm/vchiq_core.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index d7d7f4d9d57f..edcd97373809 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -252,11 +252,15 @@ next_service_by_instance(struct vchiq_state *state, struct vchiq_service *service; rcu_read_lock(); - service = __next_service_by_instance(state, instance, pidx); - if (service && kref_get_unless_zero(&service->ref_count)) - service = rcu_pointer_handoff(service); - else - service = NULL; + while (1) { + service = __next_service_by_instance(state, instance, pidx); + if (!service) + break; + if (kref_get_unless_zero(&service->ref_count)) { + service = rcu_pointer_handoff(service); + break; + } + } rcu_read_unlock(); return service; } From ac1cc6b4856ffa7ecb818b3ceb82bfc43597d613 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 11 Feb 2020 15:37:22 -0800 Subject: [PATCH 0194/2231] ACPI: button: move HIDs to acpi/button.h This makes it possible to use ACPI_BUTTON_HID_POWER in another driver. Signed-off-by: Josh Triplett Signed-off-by: Rafael J. Wysocki --- drivers/acpi/button.c | 3 --- include/acpi/button.h | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index f6925f16c4a2..00112cf15322 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -30,17 +30,14 @@ #define ACPI_BUTTON_NOTIFY_STATUS 0x80 #define ACPI_BUTTON_SUBCLASS_POWER "power" -#define ACPI_BUTTON_HID_POWER "PNP0C0C" #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button" #define ACPI_BUTTON_TYPE_POWER 0x01 #define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" -#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button" #define ACPI_BUTTON_TYPE_SLEEP 0x03 #define ACPI_BUTTON_SUBCLASS_LID "lid" -#define ACPI_BUTTON_HID_LID "PNP0C0D" #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" #define ACPI_BUTTON_TYPE_LID 0x05 diff --git a/include/acpi/button.h b/include/acpi/button.h index 340da7784cc8..af2fce5d2ee3 100644 --- a/include/acpi/button.h +++ b/include/acpi/button.h @@ -2,6 +2,10 @@ #ifndef ACPI_BUTTON_H #define ACPI_BUTTON_H +#define ACPI_BUTTON_HID_POWER "PNP0C0C" +#define ACPI_BUTTON_HID_LID "PNP0C0D" +#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" + #if IS_ENABLED(CONFIG_ACPI_BUTTON) extern int acpi_lid_open(void); #else From a1b93e890d4f3e8c134095b52954bfbccaea29a6 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 11 Feb 2020 15:38:06 -0800 Subject: [PATCH 0195/2231] ACPI: Add new tiny-power-button driver to directly signal init Virtual machines often use an ACPI power button event to tell the machine to shut down gracefully. Provide an extremely lightweight "tiny power button" driver to handle this event by signaling init directly, rather than running a separate daemon (such as acpid or systemd-logind) that adds to startup time and VM image complexity. The kernel configuration defines the default signal to send init, and userspace can change this signal via a module parameter. Suggested-by: "Rafael J. Wysocki" Signed-off-by: Josh Triplett Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 24 +++++++++++++++++ drivers/acpi/Makefile | 1 + drivers/acpi/tiny-power-button.c | 46 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 drivers/acpi/tiny-power-button.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index cc57bab146b5..ce2730d61a8f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -190,6 +190,30 @@ config ACPI_BUTTON To compile this driver as a module, choose M here: the module will be called button. +config ACPI_TINY_POWER_BUTTON + tristate "Tiny Power Button Driver" + depends on !ACPI_BUTTON + help + This driver provides a tiny alternative to the ACPI Button driver. + The tiny power button driver only handles the power button. Rather + than notifying userspace via the input layer or a netlink event, this + driver directly signals the init process to shut down. + + This driver is particularly suitable for cloud and VM environments, + which use a simulated power button to initiate a controlled poweroff, + but which may not want to run a separate userspace daemon to process + input events. + +config ACPI_TINY_POWER_BUTTON_SIGNAL + int "Tiny Power Button Signal" + depends on ACPI_TINY_POWER_BUTTON + default 38 + help + Default signal to send to init in response to the power button. + + Likely values here include 38 (SIGRTMIN+4) to power off, or 2 + (SIGINT) to simulate Ctrl+Alt+Del. + config ACPI_VIDEO tristate "Video" depends on X86 && BACKLIGHT_CLASS_DEVICE diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 33fdaf67454e..e81e1ebbfb32 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_ACPI_IPMI) += acpi_ipmi.o obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BUTTON) += button.o +obj-$(CONFIG_ACPI_TINY_POWER_BUTTON) += tiny-power-button.o obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_TAD) += acpi_tad.o diff --git a/drivers/acpi/tiny-power-button.c b/drivers/acpi/tiny-power-button.c new file mode 100644 index 000000000000..6273d73c0b59 --- /dev/null +++ b/drivers/acpi/tiny-power-button.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include + +ACPI_MODULE_NAME("tiny-power-button"); +MODULE_AUTHOR("Josh Triplett"); +MODULE_DESCRIPTION("ACPI Tiny Power Button Driver"); +MODULE_LICENSE("GPL"); + +static int power_signal __read_mostly = CONFIG_ACPI_TINY_POWER_BUTTON_SIGNAL; +module_param(power_signal, int, 0644); +MODULE_PARM_DESC(power_signal, "Power button sends this signal to init"); + +static const struct acpi_device_id tiny_power_button_device_ids[] = { + { ACPI_BUTTON_HID_POWER, 0 }, + { ACPI_BUTTON_HID_POWERF, 0 }, + { "", 0 }, +}; +MODULE_DEVICE_TABLE(acpi, tiny_power_button_device_ids); + +static int acpi_noop_add_remove(struct acpi_device *device) +{ + return 0; +} + +static void acpi_tiny_power_button_notify(struct acpi_device *device, u32 event) +{ + kill_cad_pid(power_signal, 1); +} + +static struct acpi_driver acpi_tiny_power_button_driver = { + .name = "tiny-power-button", + .class = "tiny-power-button", + .ids = tiny_power_button_device_ids, + .ops = { + .add = acpi_noop_add_remove, + .remove = acpi_noop_add_remove, + .notify = acpi_tiny_power_button_notify, + }, +}; + +module_driver(acpi_tiny_power_button_driver, + acpi_bus_register_driver, + acpi_bus_unregister_driver); From 4d4dda487a364e601a926443adf731b6fe64ae21 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:12:10 +0100 Subject: [PATCH 0196/2231] drm: i915: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/update/remove_request() instead of pm_qos_add/update/remove_request(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria Acked-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dp.c | 4 ++-- drivers/gpu/drm/i915/i915_drv.c | 12 +++++------- drivers/gpu/drm/i915/intel_sideband.c | 5 +++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c7424e2a04a3..208457005a11 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1360,7 +1360,7 @@ intel_dp_aux_xfer(struct intel_dp *intel_dp, * lowest possible wakeup latency and so prevent the cpu from going into * deep sleep states. */ - pm_qos_update_request(&i915->pm_qos, 0); + cpu_latency_qos_update_request(&i915->pm_qos, 0); intel_dp_check_edp(intel_dp); @@ -1488,7 +1488,7 @@ done: ret = recv_bytes; out: - pm_qos_update_request(&i915->pm_qos, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_update_request(&i915->pm_qos, PM_QOS_DEFAULT_VALUE); if (vdd) edp_panel_vdd_off(intel_dp, false); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f7385abdd74b..74481a189cfc 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -502,8 +502,7 @@ static int i915_driver_early_probe(struct drm_i915_private *dev_priv) mutex_init(&dev_priv->backlight_lock); mutex_init(&dev_priv->sb_lock); - pm_qos_add_request(&dev_priv->sb_qos, - PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_add_request(&dev_priv->sb_qos, PM_QOS_DEFAULT_VALUE); mutex_init(&dev_priv->av_mutex); mutex_init(&dev_priv->wm.wm_mutex); @@ -568,7 +567,7 @@ static void i915_driver_late_release(struct drm_i915_private *dev_priv) vlv_free_s0ix_state(dev_priv); i915_workqueues_cleanup(dev_priv); - pm_qos_remove_request(&dev_priv->sb_qos); + cpu_latency_qos_remove_request(&dev_priv->sb_qos); mutex_destroy(&dev_priv->sb_lock); } @@ -1226,8 +1225,7 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) } } - pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_add_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE); intel_gt_init_workarounds(dev_priv); @@ -1273,7 +1271,7 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) err_msi: if (pdev->msi_enabled) pci_disable_msi(pdev); - pm_qos_remove_request(&dev_priv->pm_qos); + cpu_latency_qos_remove_request(&dev_priv->pm_qos); err_mem_regions: intel_memory_regions_driver_release(dev_priv); err_ggtt: @@ -1296,7 +1294,7 @@ static void i915_driver_hw_remove(struct drm_i915_private *dev_priv) if (pdev->msi_enabled) pci_disable_msi(pdev); - pm_qos_remove_request(&dev_priv->pm_qos); + cpu_latency_qos_remove_request(&dev_priv->pm_qos); } /** diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c index cbfb7171d62d..0648eda309e4 100644 --- a/drivers/gpu/drm/i915/intel_sideband.c +++ b/drivers/gpu/drm/i915/intel_sideband.c @@ -60,7 +60,7 @@ static void __vlv_punit_get(struct drm_i915_private *i915) * to the Valleyview P-unit and not all sideband communications. */ if (IS_VALLEYVIEW(i915)) { - pm_qos_update_request(&i915->sb_qos, 0); + cpu_latency_qos_update_request(&i915->sb_qos, 0); on_each_cpu(ping, NULL, 1); } } @@ -68,7 +68,8 @@ static void __vlv_punit_get(struct drm_i915_private *i915) static void __vlv_punit_put(struct drm_i915_private *i915) { if (IS_VALLEYVIEW(i915)) - pm_qos_update_request(&i915->sb_qos, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_update_request(&i915->sb_qos, + PM_QOS_DEFAULT_VALUE); iosf_mbi_punit_release(); } From 6ca50a47c0962cbfda36c82663c5146e9d7bb094 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:13:17 +0100 Subject: [PATCH 0197/2231] drivers: hsi: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/remove_request() and cpu_latency_qos_request_active() instead of pm_qos_add/remove_request() and pm_qos_request_active(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria Acked-by: Sebastian Reichel --- drivers/hsi/clients/cmt_speech.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c index 9eec970cdfa5..89869c66fb9d 100644 --- a/drivers/hsi/clients/cmt_speech.c +++ b/drivers/hsi/clients/cmt_speech.c @@ -965,14 +965,13 @@ static int cs_hsi_buf_config(struct cs_hsi_iface *hi, if (old_state != hi->iface_state) { if (hi->iface_state == CS_STATE_CONFIGURED) { - pm_qos_add_request(&hi->pm_qos_req, - PM_QOS_CPU_DMA_LATENCY, + cpu_latency_qos_add_request(&hi->pm_qos_req, CS_QOS_LATENCY_FOR_DATA_USEC); local_bh_disable(); cs_hsi_read_on_data(hi); local_bh_enable(); } else if (old_state == CS_STATE_CONFIGURED) { - pm_qos_remove_request(&hi->pm_qos_req); + cpu_latency_qos_remove_request(&hi->pm_qos_req); } } return r; @@ -1075,8 +1074,8 @@ static void cs_hsi_stop(struct cs_hsi_iface *hi) WARN_ON(!cs_state_idle(hi->control_state)); WARN_ON(!cs_state_idle(hi->data_state)); - if (pm_qos_request_active(&hi->pm_qos_req)) - pm_qos_remove_request(&hi->pm_qos_req); + if (cpu_latency_qos_request_active(&hi->pm_qos_req)) + cpu_latency_qos_remove_request(&hi->pm_qos_req); spin_lock_bh(&hi->lock); cs_hsi_free_data(hi); From 7c51a06b2560b2ecd6cd01251dc478fce4bd6938 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:17:51 +0100 Subject: [PATCH 0198/2231] drivers: media: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/remove_request() instead of pm_qos_add/remove_request(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Mauro Carvalho Chehab Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/media/pci/saa7134/saa7134-video.c | 5 ++--- drivers/media/platform/via-camera.c | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 342cabf48064..a8ac94fadc14 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1008,8 +1008,7 @@ int saa7134_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) */ if ((dmaq == &dev->video_q && !vb2_is_streaming(&dev->vbi_vbq)) || (dmaq == &dev->vbi_q && !vb2_is_streaming(&dev->video_vbq))) - pm_qos_add_request(&dev->qos_request, - PM_QOS_CPU_DMA_LATENCY, 20); + cpu_latency_qos_add_request(&dev->qos_request, 20); dmaq->seq_nr = 0; return 0; @@ -1024,7 +1023,7 @@ void saa7134_vb2_stop_streaming(struct vb2_queue *vq) if ((dmaq == &dev->video_q && !vb2_is_streaming(&dev->vbi_vbq)) || (dmaq == &dev->vbi_q && !vb2_is_streaming(&dev->video_vbq))) - pm_qos_remove_request(&dev->qos_request); + cpu_latency_qos_remove_request(&dev->qos_request); } static const struct vb2_ops vb2_qops = { diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index 78841b9015ce..1cd4f7be88dd 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -646,7 +646,7 @@ static int viacam_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) * requirement which will keep the CPU out of the deeper sleep * states. */ - pm_qos_add_request(&cam->qos_request, PM_QOS_CPU_DMA_LATENCY, 50); + cpu_latency_qos_add_request(&cam->qos_request, 50); viacam_start_engine(cam); return 0; out: @@ -662,7 +662,7 @@ static void viacam_vb2_stop_streaming(struct vb2_queue *vq) struct via_camera *cam = vb2_get_drv_priv(vq); struct via_buffer *buf, *tmp; - pm_qos_remove_request(&cam->qos_request); + cpu_latency_qos_remove_request(&cam->qos_request); viacam_stop_engine(cam); list_for_each_entry_safe(buf, tmp, &cam->buffer_queue, queue) { From d1b98305916bc0b3ab3f52f4f08e4942aa7826a0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:21:53 +0100 Subject: [PATCH 0199/2231] drivers: mmc: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/remove_request() instead of pm_qos_add/remove_request(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/mmc/host/sdhci-esdhc-imx.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 382f25b2fa45..b2bdf5012c55 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1452,8 +1452,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) pdev->id_entry->driver_data; if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) - pm_qos_add_request(&imx_data->pm_qos_req, - PM_QOS_CPU_DMA_LATENCY, 0); + cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0); imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx_data->clk_ipg)) { @@ -1572,7 +1571,7 @@ disable_per_clk: clk_disable_unprepare(imx_data->clk_per); free_sdhci: if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) - pm_qos_remove_request(&imx_data->pm_qos_req); + cpu_latency_qos_remove_request(&imx_data->pm_qos_req); sdhci_pltfm_free(pdev); return err; } @@ -1595,7 +1594,7 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) clk_disable_unprepare(imx_data->clk_ahb); if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) - pm_qos_remove_request(&imx_data->pm_qos_req); + cpu_latency_qos_remove_request(&imx_data->pm_qos_req); sdhci_pltfm_free(pdev); @@ -1667,7 +1666,7 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev) clk_disable_unprepare(imx_data->clk_ahb); if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) - pm_qos_remove_request(&imx_data->pm_qos_req); + cpu_latency_qos_remove_request(&imx_data->pm_qos_req); return ret; } @@ -1680,8 +1679,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) int err; if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) - pm_qos_add_request(&imx_data->pm_qos_req, - PM_QOS_CPU_DMA_LATENCY, 0); + cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0); err = clk_prepare_enable(imx_data->clk_ahb); if (err) @@ -1714,7 +1712,7 @@ disable_ahb_clk: clk_disable_unprepare(imx_data->clk_ahb); remove_pm_qos_request: if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) - pm_qos_remove_request(&imx_data->pm_qos_req); + cpu_latency_qos_remove_request(&imx_data->pm_qos_req); return err; } #endif From 81e95ad74154232f0cda38e03e135cf1a0984888 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:24:36 +0100 Subject: [PATCH 0200/2231] drivers: net: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/update/remove_request() instead of pm_qos_add/update/remove_request(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Jeff Kirsher Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/net/ethernet/intel/e1000e/netdev.c | 13 ++++++------- drivers/net/wireless/ath/ath10k/core.c | 4 ++-- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 10 +++++----- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index db4ea58bac82..0f02c7a5ee9b 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -3280,10 +3280,10 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) dev_info(&adapter->pdev->dev, "Some CPU C-states have been disabled in order to enable jumbo frames\n"); - pm_qos_update_request(&adapter->pm_qos_req, lat); + cpu_latency_qos_update_request(&adapter->pm_qos_req, lat); } else { - pm_qos_update_request(&adapter->pm_qos_req, - PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_update_request(&adapter->pm_qos_req, + PM_QOS_DEFAULT_VALUE); } /* Enable Receives */ @@ -4636,8 +4636,7 @@ int e1000e_open(struct net_device *netdev) e1000_update_mng_vlan(adapter); /* DMA latency requirement to workaround jumbo issue */ - pm_qos_add_request(&adapter->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_add_request(&adapter->pm_qos_req, PM_QOS_DEFAULT_VALUE); /* before we allocate an interrupt, we must be ready to handle it. * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt @@ -4679,7 +4678,7 @@ int e1000e_open(struct net_device *netdev) return 0; err_req_irq: - pm_qos_remove_request(&adapter->pm_qos_req); + cpu_latency_qos_remove_request(&adapter->pm_qos_req); e1000e_release_hw_control(adapter); e1000_power_down_phy(adapter); e1000e_free_rx_resources(adapter->rx_ring); @@ -4743,7 +4742,7 @@ int e1000e_close(struct net_device *netdev) !test_bit(__E1000_TESTING, &adapter->state)) e1000e_release_hw_control(adapter); - pm_qos_remove_request(&adapter->pm_qos_req); + cpu_latency_qos_remove_request(&adapter->pm_qos_req); pm_runtime_put_sync(&pdev->dev); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 5ec16ce19b69..a202a4eea76a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1052,11 +1052,11 @@ static int ath10k_download_fw(struct ath10k *ar) } memset(&latency_qos, 0, sizeof(latency_qos)); - pm_qos_add_request(&latency_qos, PM_QOS_CPU_DMA_LATENCY, 0); + cpu_latency_qos_add_request(&latency_qos, 0); ret = ath10k_bmi_fast_download(ar, address, data, data_len); - pm_qos_remove_request(&latency_qos); + cpu_latency_qos_remove_request(&latency_qos); return ret; } diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 536cd729c086..5dfcce77d094 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -1730,7 +1730,7 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) /* the ipw2100 hardware really doesn't want power management delays * longer than 175usec */ - pm_qos_update_request(&ipw2100_pm_qos_req, 175); + cpu_latency_qos_update_request(&ipw2100_pm_qos_req, 175); /* If the interrupt is enabled, turn it off... */ spin_lock_irqsave(&priv->low_lock, flags); @@ -1875,7 +1875,8 @@ static void ipw2100_down(struct ipw2100_priv *priv) ipw2100_disable_interrupts(priv); spin_unlock_irqrestore(&priv->low_lock, flags); - pm_qos_update_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_update_request(&ipw2100_pm_qos_req, + PM_QOS_DEFAULT_VALUE); /* We have to signal any supplicant if we are disassociating */ if (associated) @@ -6566,8 +6567,7 @@ static int __init ipw2100_init(void) printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION); printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT); - pm_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); ret = pci_register_driver(&ipw2100_pci_driver); if (ret) @@ -6594,7 +6594,7 @@ static void __exit ipw2100_exit(void) &driver_attr_debug_level); #endif pci_unregister_driver(&ipw2100_pci_driver); - pm_qos_remove_request(&ipw2100_pm_qos_req); + cpu_latency_qos_remove_request(&ipw2100_pm_qos_req); } module_init(ipw2100_init); From c4c632e9074b62dbe3e756accb3bd6be13fcb61a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:26:04 +0100 Subject: [PATCH 0201/2231] drivers: spi: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/remove_request() instead of pm_qos_add/remove_request(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/spi/spi-fsl-qspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index e8a499cd1f13..02e5cba0a5bb 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -484,7 +484,7 @@ static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q) } if (needs_wakeup_wait_mode(q)) - pm_qos_add_request(&q->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0); + cpu_latency_qos_add_request(&q->pm_qos_req, 0); return 0; } @@ -492,7 +492,7 @@ static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q) static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q) { if (needs_wakeup_wait_mode(q)) - pm_qos_remove_request(&q->pm_qos_req); + cpu_latency_qos_remove_request(&q->pm_qos_req); clk_disable_unprepare(q->clk); clk_disable_unprepare(q->clk_en); From 01d2b1898c7c95863e5c3eeabb3d5688eeaefef9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:27:04 +0100 Subject: [PATCH 0202/2231] drivers: tty: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/update/remove_request() instead of pm_qos_add/update/remove_request(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Acked-by: Greg Kroah-Hartman Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/tty/serial/8250/8250_omap.c | 7 +++---- drivers/tty/serial/omap-serial.c | 9 ++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 19f8d2f9e7ba..76fe72bfb8bb 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -569,7 +569,7 @@ static void omap8250_uart_qos_work(struct work_struct *work) struct omap8250_priv *priv; priv = container_of(work, struct omap8250_priv, qos_work); - pm_qos_update_request(&priv->pm_qos_request, priv->latency); + cpu_latency_qos_update_request(&priv->pm_qos_request, priv->latency); } #ifdef CONFIG_SERIAL_8250_DMA @@ -1224,8 +1224,7 @@ static int omap8250_probe(struct platform_device *pdev) priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; priv->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; - pm_qos_add_request(&priv->pm_qos_request, PM_QOS_CPU_DMA_LATENCY, - priv->latency); + cpu_latency_qos_add_request(&priv->pm_qos_request, priv->latency); INIT_WORK(&priv->qos_work, omap8250_uart_qos_work); spin_lock_init(&priv->rx_dma_lock); @@ -1295,7 +1294,7 @@ static int omap8250_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); serial8250_unregister_port(priv->line); - pm_qos_remove_request(&priv->pm_qos_request); + cpu_latency_qos_remove_request(&priv->pm_qos_request); device_init_wakeup(&pdev->dev, false); return 0; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index ce2558767eee..e0b720ac754b 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -831,7 +831,7 @@ static void serial_omap_uart_qos_work(struct work_struct *work) struct uart_omap_port *up = container_of(work, struct uart_omap_port, qos_work); - pm_qos_update_request(&up->pm_qos_request, up->latency); + cpu_latency_qos_update_request(&up->pm_qos_request, up->latency); } static void @@ -1724,8 +1724,7 @@ static int serial_omap_probe(struct platform_device *pdev) up->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; up->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; - pm_qos_add_request(&up->pm_qos_request, - PM_QOS_CPU_DMA_LATENCY, up->latency); + cpu_latency_qos_add_request(&up->pm_qos_request, up->latency); INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); platform_set_drvdata(pdev, up); @@ -1759,7 +1758,7 @@ err_add_port: pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - pm_qos_remove_request(&up->pm_qos_request); + cpu_latency_qos_remove_request(&up->pm_qos_request); device_init_wakeup(up->dev, false); err_rs485: err_port_line: @@ -1777,7 +1776,7 @@ static int serial_omap_remove(struct platform_device *dev) pm_runtime_dont_use_autosuspend(up->dev); pm_runtime_put_sync(up->dev); pm_runtime_disable(up->dev); - pm_qos_remove_request(&up->pm_qos_request); + cpu_latency_qos_remove_request(&up->pm_qos_request); device_init_wakeup(&dev->dev, false); return 0; From 77b352456941e871ea6da01dcd65ddf796313b54 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:28:44 +0100 Subject: [PATCH 0203/2231] drivers: usb: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/remove_request() instead of pm_qos_add/remove_request(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/usb/chipidea/ci_hdrc_imx.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index d8e7eb2f97b9..a479af3ae31d 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -393,8 +393,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) } if (pdata.flags & CI_HDRC_PMQOS) - pm_qos_add_request(&data->pm_qos_req, - PM_QOS_CPU_DMA_LATENCY, 0); + cpu_latency_qos_add_request(&data->pm_qos_req, 0); ret = imx_get_clks(dev); if (ret) @@ -478,7 +477,7 @@ disable_hsic_regulator: /* don't overwrite original ret (cf. EPROBE_DEFER) */ regulator_disable(data->hsic_pad_regulator); if (pdata.flags & CI_HDRC_PMQOS) - pm_qos_remove_request(&data->pm_qos_req); + cpu_latency_qos_remove_request(&data->pm_qos_req); data->ci_pdev = NULL; return ret; } @@ -499,7 +498,7 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) if (data->ci_pdev) { imx_disable_unprepare_clks(&pdev->dev); if (data->plat_data->flags & CI_HDRC_PMQOS) - pm_qos_remove_request(&data->pm_qos_req); + cpu_latency_qos_remove_request(&data->pm_qos_req); if (data->hsic_pad_regulator) regulator_disable(data->hsic_pad_regulator); } @@ -527,7 +526,7 @@ static int __maybe_unused imx_controller_suspend(struct device *dev) imx_disable_unprepare_clks(dev); if (data->plat_data->flags & CI_HDRC_PMQOS) - pm_qos_remove_request(&data->pm_qos_req); + cpu_latency_qos_remove_request(&data->pm_qos_req); data->in_lpm = true; @@ -547,8 +546,7 @@ static int __maybe_unused imx_controller_resume(struct device *dev) } if (data->plat_data->flags & CI_HDRC_PMQOS) - pm_qos_add_request(&data->pm_qos_req, - PM_QOS_CPU_DMA_LATENCY, 0); + cpu_latency_qos_add_request(&data->pm_qos_req, 0); ret = imx_prepare_enable_clks(dev); if (ret) From 5371a79be97caacf842282fcb309675c975f428f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:34:15 +0100 Subject: [PATCH 0204/2231] sound: Call cpu_latency_qos_*() instead of pm_qos_*() Call cpu_latency_qos_add/update/remove_request() and cpu_latency_qos_request_active() instead of pm_qos_add/update/remove_request() and pm_qos_request_active(), respectively, because the latter are going to be dropped. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Acked-by: Mark Brown Acked-by: Takashi Iwai Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- sound/core/pcm_native.c | 14 +++++++------- sound/soc/intel/atom/sst/sst.c | 5 ++--- sound/soc/intel/atom/sst/sst_loader.c | 4 ++-- sound/soc/ti/omap-dmic.c | 7 ++++--- sound/soc/ti/omap-mcbsp.c | 16 ++++++++-------- sound/soc/ti/omap-mcpdm.c | 16 ++++++++-------- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 336406bcb59e..151bac1bbd0b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -748,11 +748,11 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, snd_pcm_timer_resolution_change(substream); snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP); - if (pm_qos_request_active(&substream->latency_pm_qos_req)) - pm_qos_remove_request(&substream->latency_pm_qos_req); + if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req)) + cpu_latency_qos_remove_request(&substream->latency_pm_qos_req); if ((usecs = period_to_usecs(runtime)) >= 0) - pm_qos_add_request(&substream->latency_pm_qos_req, - PM_QOS_CPU_DMA_LATENCY, usecs); + cpu_latency_qos_add_request(&substream->latency_pm_qos_req, + usecs); return 0; _error: /* hardware might be unusable from this time, @@ -821,7 +821,7 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) return -EBADFD; result = do_hw_free(substream); snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); - pm_qos_remove_request(&substream->latency_pm_qos_req); + cpu_latency_qos_remove_request(&substream->latency_pm_qos_req); return result; } @@ -2598,8 +2598,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) substream->ops->close(substream); substream->hw_opened = 0; } - if (pm_qos_request_active(&substream->latency_pm_qos_req)) - pm_qos_remove_request(&substream->latency_pm_qos_req); + if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req)) + cpu_latency_qos_remove_request(&substream->latency_pm_qos_req); if (substream->pcm_release) { substream->pcm_release(substream); substream->pcm_release = NULL; diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index 68bcec5241f7..d6563985e008 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -325,8 +325,7 @@ int sst_context_init(struct intel_sst_drv *ctx) ret = -ENOMEM; goto do_free_mem; } - pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_add_request(ctx->qos, PM_QOS_DEFAULT_VALUE); dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name); ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name, @@ -364,7 +363,7 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group); flush_scheduled_work(); destroy_workqueue(ctx->post_msg_wq); - pm_qos_remove_request(ctx->qos); + cpu_latency_qos_remove_request(ctx->qos); kfree(ctx->fw_sg_list.src); kfree(ctx->fw_sg_list.dst); ctx->fw_sg_list.list_len = 0; diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index ce11c36848c4..9b0e3739c738 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -412,7 +412,7 @@ int sst_load_fw(struct intel_sst_drv *sst_drv_ctx) return -ENOMEM; /* Prevent C-states beyond C6 */ - pm_qos_update_request(sst_drv_ctx->qos, 0); + cpu_latency_qos_update_request(sst_drv_ctx->qos, 0); sst_drv_ctx->sst_state = SST_FW_LOADING; @@ -442,7 +442,7 @@ int sst_load_fw(struct intel_sst_drv *sst_drv_ctx) restore: /* Re-enable Deeper C-states beyond C6 */ - pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE); sst_free_block(sst_drv_ctx, block); dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n"); diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c index 3f226be123d4..913579c43e9d 100644 --- a/sound/soc/ti/omap-dmic.c +++ b/sound/soc/ti/omap-dmic.c @@ -112,7 +112,7 @@ static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream, mutex_lock(&dmic->mutex); - pm_qos_remove_request(&dmic->pm_qos_req); + cpu_latency_qos_remove_request(&dmic->pm_qos_req); if (!dai->active) dmic->active = 0; @@ -230,8 +230,9 @@ static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream, struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); u32 ctrl; - if (pm_qos_request_active(&dmic->pm_qos_req)) - pm_qos_update_request(&dmic->pm_qos_req, dmic->latency); + if (cpu_latency_qos_request_active(&dmic->pm_qos_req)) + cpu_latency_qos_update_request(&dmic->pm_qos_req, + dmic->latency); /* Configure uplink threshold */ omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold); diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c index 26b503bbdb5f..302d5c493c29 100644 --- a/sound/soc/ti/omap-mcbsp.c +++ b/sound/soc/ti/omap-mcbsp.c @@ -836,10 +836,10 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream, int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; if (mcbsp->latency[stream2]) - pm_qos_update_request(&mcbsp->pm_qos_req, - mcbsp->latency[stream2]); + cpu_latency_qos_update_request(&mcbsp->pm_qos_req, + mcbsp->latency[stream2]); else if (mcbsp->latency[stream1]) - pm_qos_remove_request(&mcbsp->pm_qos_req); + cpu_latency_qos_remove_request(&mcbsp->pm_qos_req); mcbsp->latency[stream1] = 0; @@ -863,10 +863,10 @@ static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream, if (!latency || mcbsp->latency[stream1] < latency) latency = mcbsp->latency[stream1]; - if (pm_qos_request_active(pm_qos_req)) - pm_qos_update_request(pm_qos_req, latency); + if (cpu_latency_qos_request_active(pm_qos_req)) + cpu_latency_qos_update_request(pm_qos_req, latency); else if (latency) - pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency); + cpu_latency_qos_add_request(pm_qos_req, latency); return 0; } @@ -1434,8 +1434,8 @@ static int asoc_mcbsp_remove(struct platform_device *pdev) if (mcbsp->pdata->ops && mcbsp->pdata->ops->free) mcbsp->pdata->ops->free(mcbsp->id); - if (pm_qos_request_active(&mcbsp->pm_qos_req)) - pm_qos_remove_request(&mcbsp->pm_qos_req); + if (cpu_latency_qos_request_active(&mcbsp->pm_qos_req)) + cpu_latency_qos_remove_request(&mcbsp->pm_qos_req); if (mcbsp->pdata->buffer_size) sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); diff --git a/sound/soc/ti/omap-mcpdm.c b/sound/soc/ti/omap-mcpdm.c index a726cd7a8252..d7ac4df6f2d9 100644 --- a/sound/soc/ti/omap-mcpdm.c +++ b/sound/soc/ti/omap-mcpdm.c @@ -281,10 +281,10 @@ static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, } if (mcpdm->latency[stream2]) - pm_qos_update_request(&mcpdm->pm_qos_req, - mcpdm->latency[stream2]); + cpu_latency_qos_update_request(&mcpdm->pm_qos_req, + mcpdm->latency[stream2]); else if (mcpdm->latency[stream1]) - pm_qos_remove_request(&mcpdm->pm_qos_req); + cpu_latency_qos_remove_request(&mcpdm->pm_qos_req); mcpdm->latency[stream1] = 0; @@ -386,10 +386,10 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream, if (!latency || mcpdm->latency[stream1] < latency) latency = mcpdm->latency[stream1]; - if (pm_qos_request_active(pm_qos_req)) - pm_qos_update_request(pm_qos_req, latency); + if (cpu_latency_qos_request_active(pm_qos_req)) + cpu_latency_qos_update_request(pm_qos_req, latency); else if (latency) - pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency); + cpu_latency_qos_add_request(pm_qos_req, latency); if (!omap_mcpdm_active(mcpdm)) { omap_mcpdm_start(mcpdm); @@ -451,8 +451,8 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai) free_irq(mcpdm->irq, (void *)mcpdm); pm_runtime_disable(mcpdm->dev); - if (pm_qos_request_active(&mcpdm->pm_qos_req)) - pm_qos_remove_request(&mcpdm->pm_qos_req); + if (cpu_latency_qos_request_active(&mcpdm->pm_qos_req)) + cpu_latency_qos_remove_request(&mcpdm->pm_qos_req); return 0; } From 67b06ba01857ed077e1a66bfa139156e7c68bab2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:35:04 +0100 Subject: [PATCH 0205/2231] PM: QoS: Drop PM_QOS_CPU_DMA_LATENCY and rename related functions Drop the PM QoS classes enum including PM_QOS_CPU_DMA_LATENCY, drop the wrappers around pm_qos_request(), pm_qos_request_active(), and pm_qos_add/update/remove_request() introduced previously, rename these functions, respectively, to cpu_latency_qos_limit(), cpu_latency_qos_request_active(), and cpu_latency_qos_add/update/remove_request(), and update their kerneldoc comments. [While at it, drop some useless comments from these functions.] No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 47 +++----------------- kernel/power/qos.c | 98 +++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 91 deletions(-) diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 63d39e66f95d..e0ca4d780457 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -9,14 +9,6 @@ #include #include -enum { - PM_QOS_RESERVED = 0, - PM_QOS_CPU_DMA_LATENCY, - - /* insert new class ID */ - PM_QOS_NUM_CLASSES, -}; - enum pm_qos_flags_status { PM_QOS_FLAGS_UNDEFINED = -1, PM_QOS_FLAGS_NONE, @@ -144,40 +136,11 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, struct pm_qos_flags_request *req, enum pm_qos_req_action action, s32 val); -void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, - s32 value); -void pm_qos_update_request(struct pm_qos_request *req, - s32 new_value); -void pm_qos_remove_request(struct pm_qos_request *req); -s32 pm_qos_request(int pm_qos_class); -int pm_qos_request_active(struct pm_qos_request *req); - -static inline void cpu_latency_qos_add_request(struct pm_qos_request *req, - s32 value) -{ - pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, value); -} - -static inline void cpu_latency_qos_update_request(struct pm_qos_request *req, - s32 new_value) -{ - pm_qos_update_request(req, new_value); -} - -static inline void cpu_latency_qos_remove_request(struct pm_qos_request *req) -{ - pm_qos_remove_request(req); -} - -static inline bool cpu_latency_qos_request_active(struct pm_qos_request *req) -{ - return pm_qos_request_active(req); -} - -static inline s32 cpu_latency_qos_limit(void) -{ - return pm_qos_request(PM_QOS_CPU_DMA_LATENCY); -} +s32 cpu_latency_qos_limit(void); +bool cpu_latency_qos_request_active(struct pm_qos_request *req); +void cpu_latency_qos_add_request(struct pm_qos_request *req, s32 value); +void cpu_latency_qos_update_request(struct pm_qos_request *req, s32 new_value); +void cpu_latency_qos_remove_request(struct pm_qos_request *req); #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 7bb55aca03bb..7374c76f409a 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -230,21 +230,25 @@ static struct pm_qos_constraints cpu_latency_constraints = { }; /** - * pm_qos_request - returns current system wide qos expectation - * @pm_qos_class: Ignored. - * - * This function returns the current target value. + * cpu_latency_qos_limit - Return current system-wide CPU latency QoS limit. */ -s32 pm_qos_request(int pm_qos_class) +s32 cpu_latency_qos_limit(void) { return pm_qos_read_value(&cpu_latency_constraints); } -int pm_qos_request_active(struct pm_qos_request *req) +/** + * cpu_latency_qos_request_active - Check the given PM QoS request. + * @req: PM QoS request to check. + * + * Return: 'true' if @req has been added to the CPU latency QoS list, 'false' + * otherwise. + */ +bool cpu_latency_qos_request_active(struct pm_qos_request *req) { return req->qos == &cpu_latency_constraints; } -EXPORT_SYMBOL_GPL(pm_qos_request_active); +EXPORT_SYMBOL_GPL(cpu_latency_qos_request_active); static void cpu_latency_qos_apply(struct pm_qos_request *req, enum pm_qos_req_action action, s32 value) @@ -255,25 +259,24 @@ static void cpu_latency_qos_apply(struct pm_qos_request *req, } /** - * pm_qos_add_request - inserts new qos request into the list - * @req: pointer to a preallocated handle - * @pm_qos_class: Ignored. - * @value: defines the qos request + * cpu_latency_qos_add_request - Add new CPU latency QoS request. + * @req: Pointer to a preallocated handle. + * @value: Requested constraint value. * - * This function inserts a new entry in the PM_QOS_CPU_DMA_LATENCY list of - * requested QoS performance characteristics. It recomputes the aggregate QoS - * expectations for the PM_QOS_CPU_DMA_LATENCY list and initializes the @req - * handle. Caller needs to save this handle for later use in updates and - * removal. + * Use @value to initialize the request handle pointed to by @req, insert it as + * a new entry to the CPU latency QoS list and recompute the effective QoS + * constraint for that list. + * + * Callers need to save the handle for later use in updates and removal of the + * QoS request represented by it. */ -void pm_qos_add_request(struct pm_qos_request *req, - int pm_qos_class, s32 value) +void cpu_latency_qos_add_request(struct pm_qos_request *req, s32 value) { - if (!req) /*guard against callers passing in null */ + if (!req) return; - if (pm_qos_request_active(req)) { - WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n"); + if (cpu_latency_qos_request_active(req)) { + WARN(1, KERN_ERR "%s called for already added request\n", __func__); return; } @@ -282,25 +285,24 @@ void pm_qos_add_request(struct pm_qos_request *req, req->qos = &cpu_latency_constraints; cpu_latency_qos_apply(req, PM_QOS_ADD_REQ, value); } -EXPORT_SYMBOL_GPL(pm_qos_add_request); +EXPORT_SYMBOL_GPL(cpu_latency_qos_add_request); /** - * pm_qos_update_request - modifies an existing qos request - * @req : handle to list element holding a pm_qos request to use - * @value: defines the qos request + * cpu_latency_qos_update_request - Modify existing CPU latency QoS request. + * @req : QoS request to update. + * @new_value: New requested constraint value. * - * Updates an existing qos request for the PM_QOS_CPU_DMA_LATENCY list along - * with updating the target PM_QOS_CPU_DMA_LATENCY value. - * - * Attempts are made to make this code callable on hot code paths. + * Use @new_value to update the QoS request represented by @req in the CPU + * latency QoS list along with updating the effective constraint value for that + * list. */ -void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) +void cpu_latency_qos_update_request(struct pm_qos_request *req, s32 new_value) { - if (!req) /*guard against callers passing in null */ + if (!req) return; - if (!pm_qos_request_active(req)) { - WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n"); + if (!cpu_latency_qos_request_active(req)) { + WARN(1, KERN_ERR "%s called for unknown object\n", __func__); return; } @@ -311,24 +313,22 @@ void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) cpu_latency_qos_apply(req, PM_QOS_UPDATE_REQ, new_value); } -EXPORT_SYMBOL_GPL(pm_qos_update_request); +EXPORT_SYMBOL_GPL(cpu_latency_qos_update_request); /** - * pm_qos_remove_request - modifies an existing qos request - * @req: handle to request list element + * cpu_latency_qos_remove_request - Remove existing CPU latency QoS request. + * @req: QoS request to remove. * - * Will remove pm qos request from the list of constraints and - * recompute the current target value for PM_QOS_CPU_DMA_LATENCY. Call this - * on slow code paths. + * Remove the CPU latency QoS request represented by @req from the CPU latency + * QoS list along with updating the effective constraint value for that list. */ -void pm_qos_remove_request(struct pm_qos_request *req) +void cpu_latency_qos_remove_request(struct pm_qos_request *req) { - if (!req) /*guard against callers passing in null */ + if (!req) return; - /* silent return to keep pcm code cleaner */ - if (!pm_qos_request_active(req)) { - WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n"); + if (!cpu_latency_qos_request_active(req)) { + WARN(1, KERN_ERR "%s called for unknown object\n", __func__); return; } @@ -337,7 +337,7 @@ void pm_qos_remove_request(struct pm_qos_request *req) cpu_latency_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } -EXPORT_SYMBOL_GPL(pm_qos_remove_request); +EXPORT_SYMBOL_GPL(cpu_latency_qos_remove_request); /* User space interface to the CPU latency QoS via misc device. */ @@ -349,7 +349,7 @@ static int cpu_latency_qos_open(struct inode *inode, struct file *filp) if (!req) return -ENOMEM; - pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_add_request(req, PM_QOS_DEFAULT_VALUE); filp->private_data = req; return 0; @@ -361,7 +361,7 @@ static int cpu_latency_qos_release(struct inode *inode, struct file *filp) filp->private_data = NULL; - pm_qos_remove_request(req); + cpu_latency_qos_remove_request(req); kfree(req); return 0; @@ -374,7 +374,7 @@ static ssize_t cpu_latency_qos_read(struct file *filp, char __user *buf, unsigned long flags; s32 value; - if (!req || !pm_qos_request_active(req)) + if (!req || !cpu_latency_qos_request_active(req)) return -EINVAL; spin_lock_irqsave(&pm_qos_lock, flags); @@ -400,7 +400,7 @@ static ssize_t cpu_latency_qos_write(struct file *filp, const char __user *buf, return ret; } - pm_qos_update_request(filp->private_data, value); + cpu_latency_qos_update_request(filp->private_data, value); return count; } From fe52de36dc5d60960230cabec88c357113d9c32a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:35:39 +0100 Subject: [PATCH 0206/2231] PM: QoS: Update file information comments Update the file information comments in include/linux/pm_qos.h and kernel/power/qos.c by adding titles along with copyright and authors information to them and changing the qos.c description to better reflect its contents (outdated information is dropped from it in particular). Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 15 +++++++++++---- kernel/power/qos.c | 34 ++++++++++++---------------------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index e0ca4d780457..df065db3f57a 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -1,10 +1,17 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Definitions related to Power Management Quality of Service (PM QoS). + * + * Copyright (C) 2020 Intel Corporation + * + * Authors: + * Mark Gross + * Rafael J. Wysocki + */ + #ifndef _LINUX_PM_QOS_H #define _LINUX_PM_QOS_H -/* interface for the pm_qos_power infrastructure of the linux kernel. - * - * Mark Gross - */ + #include #include #include diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 7374c76f409a..ef73573db43d 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -1,31 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * This module exposes the interface to kernel space for specifying - * QoS dependencies. It provides infrastructure for registration of: + * Power Management Quality of Service (PM QoS) support base. * - * Dependents on a QoS value : register requests - * Watchers of QoS value : get notified when target QoS value changes + * Copyright (C) 2020 Intel Corporation * - * This QoS design is best effort based. Dependents register their QoS needs. - * Watchers register to keep track of the current QoS needs of the system. + * Authors: + * Mark Gross + * Rafael J. Wysocki * - * There are 3 basic classes of QoS parameter: latency, timeout, throughput - * each have defined units: - * latency: usec - * timeout: usec <-- currently not used. - * throughput: kbs (kilo byte / sec) + * Provided here is an interface for specifying PM QoS dependencies. It allows + * entities depending on QoS constraints to register their requests which are + * aggregated as appropriate to produce effective constraints (target values) + * that can be monitored by entities needing to respect them, either by polling + * or through a built-in notification mechanism. * - * There are lists of pm_qos_objects each one wrapping requests, notifiers - * - * User mode requests on a QOS parameter register themselves to the - * subsystem by opening the device node /dev/... and writing there request to - * the node. As long as the process holds a file handle open to the node the - * client continues to be accounted for. Upon file release the usermode - * request is removed and a new qos target is computed. This way when the - * request that the application has is cleaned up when closes the file - * pointer or exits the pm_qos_object will get an opportunity to clean up. - * - * Mark Gross + * In addition to the basic functionality, more specific interfaces for managing + * global CPU latency QoS requests and frequency QoS requests are provided. */ /*#define DEBUG*/ From b8e6e27c626e60bb5708ce8c974b58c485c32ac7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:36:35 +0100 Subject: [PATCH 0207/2231] Documentation: PM: QoS: Update to reflect previous code changes Update the PM QoS documentation to reflect the previous code changes regarding the removal of PM QoS classes and the CPU latency QoS API rework. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- Documentation/admin-guide/pm/cpuidle.rst | 73 ++++++++++---------- Documentation/power/pm_qos_interface.rst | 84 +++++++++++------------- 2 files changed, 74 insertions(+), 83 deletions(-) diff --git a/Documentation/admin-guide/pm/cpuidle.rst b/Documentation/admin-guide/pm/cpuidle.rst index 6a06dc473dd6..5605cc6f9560 100644 --- a/Documentation/admin-guide/pm/cpuidle.rst +++ b/Documentation/admin-guide/pm/cpuidle.rst @@ -583,20 +583,17 @@ Power Management Quality of Service for CPUs The power management quality of service (PM QoS) framework in the Linux kernel allows kernel code and user space processes to set constraints on various energy-efficiency features of the kernel to prevent performance from dropping -below a required level. The PM QoS constraints can be set globally, in -predefined categories referred to as PM QoS classes, or against individual -devices. +below a required level. CPU idle time management can be affected by PM QoS in two ways, through the -global constraint in the ``PM_QOS_CPU_DMA_LATENCY`` class and through the -resume latency constraints for individual CPUs. Kernel code (e.g. device -drivers) can set both of them with the help of special internal interfaces -provided by the PM QoS framework. User space can modify the former by opening -the :file:`cpu_dma_latency` special device file under :file:`/dev/` and writing -a binary value (interpreted as a signed 32-bit integer) to it. In turn, the -resume latency constraint for a CPU can be modified by user space by writing a -string (representing a signed 32-bit integer) to the -:file:`power/pm_qos_resume_latency_us` file under +global CPU latency limit and through the resume latency constraints for +individual CPUs. Kernel code (e.g. device drivers) can set both of them with +the help of special internal interfaces provided by the PM QoS framework. User +space can modify the former by opening the :file:`cpu_dma_latency` special +device file under :file:`/dev/` and writing a binary value (interpreted as a +signed 32-bit integer) to it. In turn, the resume latency constraint for a CPU +can be modified from user space by writing a string (representing a signed +32-bit integer) to the :file:`power/pm_qos_resume_latency_us` file under :file:`/sys/devices/system/cpu/cpu/` in ``sysfs``, where the CPU number ```` is allocated at the system initialization time. Negative values will be rejected in both cases and, also in both cases, the written integer @@ -605,32 +602,34 @@ number will be interpreted as a requested PM QoS constraint in microseconds. The requested value is not automatically applied as a new constraint, however, as it may be less restrictive (greater in this particular case) than another constraint previously requested by someone else. For this reason, the PM QoS -framework maintains a list of requests that have been made so far in each -global class and for each device, aggregates them and applies the effective -(minimum in this particular case) value as the new constraint. +framework maintains a list of requests that have been made so far for the +global CPU latency limit and for each individual CPU, aggregates them and +applies the effective (minimum in this particular case) value as the new +constraint. In fact, opening the :file:`cpu_dma_latency` special device file causes a new -PM QoS request to be created and added to the priority list of requests in the -``PM_QOS_CPU_DMA_LATENCY`` class and the file descriptor coming from the -"open" operation represents that request. If that file descriptor is then -used for writing, the number written to it will be associated with the PM QoS -request represented by it as a new requested constraint value. Next, the -priority list mechanism will be used to determine the new effective value of -the entire list of requests and that effective value will be set as a new -constraint. Thus setting a new requested constraint value will only change the -real constraint if the effective "list" value is affected by it. In particular, -for the ``PM_QOS_CPU_DMA_LATENCY`` class it only affects the real constraint if -it is the minimum of the requested constraints in the list. The process holding -a file descriptor obtained by opening the :file:`cpu_dma_latency` special device -file controls the PM QoS request associated with that file descriptor, but it -controls this particular PM QoS request only. +PM QoS request to be created and added to a global priority list of CPU latency +limit requests and the file descriptor coming from the "open" operation +represents that request. If that file descriptor is then used for writing, the +number written to it will be associated with the PM QoS request represented by +it as a new requested limit value. Next, the priority list mechanism will be +used to determine the new effective value of the entire list of requests and +that effective value will be set as a new CPU latency limit. Thus requesting a +new limit value will only change the real limit if the effective "list" value is +affected by it, which is the case if it is the minimum of the requested values +in the list. + +The process holding a file descriptor obtained by opening the +:file:`cpu_dma_latency` special device file controls the PM QoS request +associated with that file descriptor, but it controls this particular PM QoS +request only. Closing the :file:`cpu_dma_latency` special device file or, more precisely, the file descriptor obtained while opening it, causes the PM QoS request associated -with that file descriptor to be removed from the ``PM_QOS_CPU_DMA_LATENCY`` -class priority list and destroyed. If that happens, the priority list mechanism -will be used, again, to determine the new effective value for the whole list -and that value will become the new real constraint. +with that file descriptor to be removed from the global priority list of CPU +latency limit requests and destroyed. If that happens, the priority list +mechanism will be used again, to determine the new effective value for the whole +list and that value will become the new limit. In turn, for each CPU there is one resume latency PM QoS request associated with the :file:`power/pm_qos_resume_latency_us` file under @@ -647,10 +646,10 @@ CPU in question every time the list of requests is updated this way or another (there may be other requests coming from kernel code in that list). CPU idle time governors are expected to regard the minimum of the global -effective ``PM_QOS_CPU_DMA_LATENCY`` class constraint and the effective -resume latency constraint for the given CPU as the upper limit for the exit -latency of the idle states they can select for that CPU. They should never -select any idle states with exit latency beyond that limit. +(effective) CPU latency limit and the effective resume latency constraint for +the given CPU as the upper limit for the exit latency of the idle states that +they are allowed to select for that CPU. They should never select any idle +states with exit latency beyond that limit. Idle States Control Via Kernel Command Line diff --git a/Documentation/power/pm_qos_interface.rst b/Documentation/power/pm_qos_interface.rst index 0d62d506caf0..064f668fbdab 100644 --- a/Documentation/power/pm_qos_interface.rst +++ b/Documentation/power/pm_qos_interface.rst @@ -7,86 +7,78 @@ performance expectations by drivers, subsystems and user space applications on one of the parameters. Two different PM QoS frameworks are available: -1. PM QoS classes for cpu_dma_latency +1. CPU latency QoS. 2. The per-device PM QoS framework provides the API to manage the per-device latency constraints and PM QoS flags. -Each parameters have defined units: - - * latency: usec - * timeout: usec - * throughput: kbs (kilo bit / sec) - * memory bandwidth: mbs (mega bit / sec) +The latency unit used in the PM QoS framework is the microsecond (usec). 1. PM QoS framework =================== -The infrastructure exposes multiple misc device nodes one per implemented -parameter. The set of parameters implement is defined by pm_qos_power_init() -and pm_qos_params.h. This is done because having the available parameters -being runtime configurable or changeable from a driver was seen as too easy to -abuse. +A global list of CPU latency QoS requests is maintained along with an aggregated +(effective) target value. The aggregated target value is updated with changes +to the request list or elements of the list. For CPU latency QoS, the +aggregated target value is simply the min of the request values held in the list +elements. -For each parameter a list of performance requests is maintained along with -an aggregated target value. The aggregated target value is updated with -changes to the request list or elements of the list. Typically the -aggregated target value is simply the max or min of the request values held -in the parameter list elements. Note: the aggregated target value is implemented as an atomic variable so that reading the aggregated value does not require any locking mechanism. +From kernel space the use of this interface is simple: -From kernel mode the use of this interface is simple: +void cpu_latency_qos_add_request(handle, target_value): + Will insert an element into the CPU latency QoS list with the target value. + Upon change to this list the new target is recomputed and any registered + notifiers are called only if the target value is now different. + Clients of PM QoS need to save the returned handle for future use in other + PM QoS API functions. -void pm_qos_add_request(handle, param_class, target_value): - Will insert an element into the list for that identified PM QoS class with the - target value. Upon change to this list the new target is recomputed and any - registered notifiers are called only if the target value is now different. - Clients of pm_qos need to save the returned handle for future use in other - pm_qos API functions. - -void pm_qos_update_request(handle, new_target_value): +void cpu_latency_qos_update_request(handle, new_target_value): Will update the list element pointed to by the handle with the new target value and recompute the new aggregated target, calling the notification tree if the target is changed. -void pm_qos_remove_request(handle): +void cpu_latency_qos_remove_request(handle): Will remove the element. After removal it will update the aggregate target and call the notification tree if the target was changed as a result of removing the request. -int pm_qos_request(param_class): - Returns the aggregated value for a given PM QoS class. +int cpu_latency_qos_limit(): + Returns the aggregated value for the CPU latency QoS. -int pm_qos_request_active(handle): - Returns if the request is still active, i.e. it has not been removed from a - PM QoS class constraints list. +int cpu_latency_qos_request_active(handle): + Returns if the request is still active, i.e. it has not been removed from the + CPU latency QoS list. -int pm_qos_add_notifier(param_class, notifier): - Adds a notification callback function to the PM QoS class. The callback is - called when the aggregated value for the PM QoS class is changed. +int cpu_latency_qos_add_notifier(notifier): + Adds a notification callback function to the CPU latency QoS. The callback is + called when the aggregated value for the CPU latency QoS is changed. -int pm_qos_remove_notifier(int param_class, notifier): - Removes the notification callback function for the PM QoS class. +int cpu_latency_qos_remove_notifier(notifier): + Removes the notification callback function from the CPU latency QoS. -From user mode: +From user space: -Only processes can register a pm_qos request. To provide for automatic +The infrastructure exposes one device node, /dev/cpu_dma_latency, for the CPU +latency QoS. + +Only processes can register a PM QoS request. To provide for automatic cleanup of a process, the interface requires the process to register its -parameter requests in the following way: +parameter requests as follows. -To register the default pm_qos target for the specific parameter, the process -must open /dev/cpu_dma_latency +To register the default PM QoS target for the CPU latency QoS, the process must +open /dev/cpu_dma_latency. As long as the device node is held open that process has a registered request on the parameter. -To change the requested target value the process needs to write an s32 value to -the open device node. Alternatively the user mode program could write a hex -string for the value using 10 char long format e.g. "0x12345678". This -translates to a pm_qos_update_request call. +To change the requested target value, the process needs to write an s32 value to +the open device node. Alternatively, it can write a hex string for the value +using the 10 char long format e.g. "0x12345678". This translates to a +cpu_latency_qos_update_request() call. To remove the user mode request for a target value simply close the device node. From 814d51f8889bc4afa75f647eeffd5ff0a5620e8d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:37:11 +0100 Subject: [PATCH 0208/2231] PM: QoS: Make CPU latency QoS depend on CONFIG_CPU_IDLE Because cpuidle is the only user of the effective constraint coming from the CPU latency QoS, add #ifdef CONFIG_CPU_IDLE around that code to avoid building it unnecessarily. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 13 +++++++++++++ kernel/power/qos.c | 2 ++ 2 files changed, 15 insertions(+) diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index df065db3f57a..4a69d4af3ff8 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -143,11 +143,24 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, struct pm_qos_flags_request *req, enum pm_qos_req_action action, s32 val); +#ifdef CONFIG_CPU_IDLE s32 cpu_latency_qos_limit(void); bool cpu_latency_qos_request_active(struct pm_qos_request *req); void cpu_latency_qos_add_request(struct pm_qos_request *req, s32 value); void cpu_latency_qos_update_request(struct pm_qos_request *req, s32 new_value); void cpu_latency_qos_remove_request(struct pm_qos_request *req); +#else +static inline s32 cpu_latency_qos_limit(void) { return INT_MAX; } +static inline bool cpu_latency_qos_request_active(struct pm_qos_request *req) +{ + return false; +} +static inline void cpu_latency_qos_add_request(struct pm_qos_request *req, + s32 value) {} +static inline void cpu_latency_qos_update_request(struct pm_qos_request *req, + s32 new_value) {} +static inline void cpu_latency_qos_remove_request(struct pm_qos_request *req) {} +#endif #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index ef73573db43d..32927682bcc4 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -209,6 +209,7 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, return prev_value != curr_value; } +#ifdef CONFIG_CPU_IDLE /* Definitions related to the CPU latency QoS. */ static struct pm_qos_constraints cpu_latency_constraints = { @@ -421,6 +422,7 @@ static int __init cpu_latency_qos_init(void) return ret; } late_initcall(cpu_latency_qos_init); +#endif /* CONFIG_CPU_IDLE */ /* Definitions related to the frequency QoS below. */ From c2a9fca17e4c021e526cc52b78e0f30105024b82 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 17:19:11 -0600 Subject: [PATCH 0209/2231] thunderbolt: eeprom: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Mika Westerberg --- drivers/thunderbolt/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 921d164b3f35..b451a5aa90b5 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -247,7 +247,7 @@ struct tb_drom_entry_header { struct tb_drom_entry_generic { struct tb_drom_entry_header header; - u8 data[0]; + u8 data[]; } __packed; struct tb_drom_entry_port { From a8cf1723c4b794f49d0906f9ddc9d1ac52af4ee2 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 15 Jan 2020 14:45:41 +0100 Subject: [PATCH 0210/2231] dt-bindings: iio: adc: stm32-adc: convert bindings to json-schema Convert the STM32 ADC binding to DT schema format using json-schema Signed-off-by: Fabrice Gasnier Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/st,stm32-adc.txt | 149 ------ .../bindings/iio/adc/st,stm32-adc.yaml | 458 ++++++++++++++++++ 2 files changed, 458 insertions(+), 149 deletions(-) delete mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt deleted file mode 100644 index 8de933146771..000000000000 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt +++ /dev/null @@ -1,149 +0,0 @@ -STMicroelectronics STM32 ADC device driver - -STM32 ADC is a successive approximation analog-to-digital converter. -It has several multiplexed input channels. Conversions can be performed -in single, continuous, scan or discontinuous mode. Result of the ADC is -stored in a left-aligned or right-aligned 32-bit data register. -Conversions can be launched in software or using hardware triggers. - -The analog watchdog feature allows the application to detect if the input -voltage goes beyond the user-defined, higher or lower thresholds. - -Each STM32 ADC block can have up to 3 ADC instances. - -Each instance supports two contexts to manage conversions, each one has its -own configurable sequence and trigger: -- regular conversion can be done in sequence, running in background -- injected conversions have higher priority, and so have the ability to - interrupt regular conversion sequence (either triggered in SW or HW). - Regular sequence is resumed, in case it has been interrupted. - -Contents of a stm32 adc root node: ------------------------------------ -Required properties: -- compatible: Should be one of: - "st,stm32f4-adc-core" - "st,stm32h7-adc-core" - "st,stm32mp1-adc-core" -- reg: Offset and length of the ADC block register set. -- interrupts: One or more interrupts for ADC block. Some parts like stm32f4 - and stm32h7 share a common ADC interrupt line. stm32mp1 has two separate - interrupt lines, one for each ADC within ADC block. -- clocks: Core can use up to two clocks, depending on part used: - - "adc" clock: for the analog circuitry, common to all ADCs. - It's required on stm32f4. - It's optional on stm32h7. - - "bus" clock: for registers access, common to all ADCs. - It's not present on stm32f4. - It's required on stm32h7. -- clock-names: Must be "adc" and/or "bus" depending on part used. -- interrupt-controller: Identifies the controller node as interrupt-parent -- vdda-supply: Phandle to the vdda input analog voltage. -- vref-supply: Phandle to the vref input analog reference voltage. -- #interrupt-cells = <1>; -- #address-cells = <1>; -- #size-cells = <0>; - -Optional properties: -- A pinctrl state named "default" for each ADC channel may be defined to set - inX ADC pins in mode of operation for analog input on external pin. -- booster-supply: Phandle to the embedded booster regulator that can be used - to supply ADC analog input switches on stm32h7 and stm32mp1. -- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC - analog input switches on stm32mp1. -- st,syscfg: Phandle to system configuration controller. It can be used to - control the analog circuitry on stm32mp1. -- st,max-clk-rate-hz: Allow to specify desired max clock rate used by analog - circuitry. - -Contents of a stm32 adc child node: ------------------------------------ -An ADC block node should contain at least one subnode, representing an -ADC instance available on the machine. - -Required properties: -- compatible: Should be one of: - "st,stm32f4-adc" - "st,stm32h7-adc" - "st,stm32mp1-adc" -- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200). -- clocks: Input clock private to this ADC instance. It's required only on - stm32f4, that has per instance clock input for registers access. -- interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or - 2 for adc@200). -- st,adc-channels: List of single-ended channels muxed for this ADC. - It can have up to 16 channels on stm32f4 or 20 channels on stm32h7, numbered - from 0 to 15 or 19 (resp. for in0..in15 or in0..in19). -- st,adc-diff-channels: List of differential channels muxed for this ADC. - Depending on part used, some channels can be configured as differential - instead of single-ended (e.g. stm32h7). List here positive and negative - inputs pairs as , ,... vinp and vinn are numbered - from 0 to 19 on stm32h7) - Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is required. - Both properties can be used together. Some channels can be used as - single-ended and some other ones as differential (mixed). But channels - can't be configured both as single-ended and differential (invalid). -- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in - Documentation/devicetree/bindings/iio/iio-bindings.txt - -Optional properties: -- dmas: Phandle to dma channel for this ADC instance. - See ../../dma/dma.txt for details. -- dma-names: Must be "rx" when dmas property is being used. -- assigned-resolution-bits: Resolution (bits) to use for conversions. Must - match device available resolutions: - * can be 6, 8, 10 or 12 on stm32f4 - * can be 8, 10, 12, 14 or 16 on stm32h7 - Default is maximum resolution if unset. -- st,min-sample-time-nsecs: Minimum sampling time in nanoseconds. - Depending on hardware (board) e.g. high/low analog input source impedance, - fine tune of ADC sampling time may be recommended. - This can be either one value or an array that matches 'st,adc-channels' list, - to set sample time resp. for all channels, or independently for each channel. - -Example: - adc: adc@40012000 { - compatible = "st,stm32f4-adc-core"; - reg = <0x40012000 0x400>; - interrupts = <18>; - clocks = <&rcc 0 168>; - clock-names = "adc"; - vref-supply = <®_vref>; - interrupt-controller; - pinctrl-names = "default"; - pinctrl-0 = <&adc3_in8_pin>; - - #interrupt-cells = <1>; - #address-cells = <1>; - #size-cells = <0>; - - adc@0 { - compatible = "st,stm32f4-adc"; - #io-channel-cells = <1>; - reg = <0x0>; - clocks = <&rcc 0 168>; - interrupt-parent = <&adc>; - interrupts = <0>; - st,adc-channels = <8>; - dmas = <&dma2 0 0 0x400 0x0>; - dma-names = "rx"; - assigned-resolution-bits = <8>; - }; - ... - other adc child nodes follow... - }; - -Example to setup: -- channel 1 as single-ended -- channels 2 & 3 as differential (with resp. 6 & 7 negative inputs) - - adc: adc@40022000 { - compatible = "st,stm32h7-adc-core"; - ... - adc1: adc@0 { - compatible = "st,stm32h7-adc"; - ... - st,adc-channels = <1>; - st,adc-diff-channels = <2 6>, <3 7>; - }; - }; diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml new file mode 100644 index 000000000000..933ba37944d7 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -0,0 +1,458 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/bindings/iio/adc/st,stm32-adc.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: STMicroelectronics STM32 ADC bindings + +description: | + STM32 ADC is a successive approximation analog-to-digital converter. + It has several multiplexed input channels. Conversions can be performed + in single, continuous, scan or discontinuous mode. Result of the ADC is + stored in a left-aligned or right-aligned 32-bit data register. + Conversions can be launched in software or using hardware triggers. + + The analog watchdog feature allows the application to detect if the input + voltage goes beyond the user-defined, higher or lower thresholds. + + Each STM32 ADC block can have up to 3 ADC instances. + +maintainers: + - Fabrice Gasnier + +properties: + compatible: + enum: + - st,stm32f4-adc-core + - st,stm32h7-adc-core + - st,stm32mp1-adc-core + + reg: + maxItems: 1 + + interrupts: + description: | + One or more interrupts for ADC block, depending on part used: + - stm32f4 and stm32h7 share a common ADC interrupt line. + - stm32mp1 has two separate interrupt lines, one for each ADC within + ADC block. + minItems: 1 + maxItems: 2 + + clocks: + description: | + Core can use up to two clocks, depending on part used: + - "adc" clock: for the analog circuitry, common to all ADCs. + It's required on stm32f4. + It's optional on stm32h7 and stm32mp1. + - "bus" clock: for registers access, common to all ADCs. + It's not present on stm32f4. + It's required on stm32h7 and stm32mp1. + + clock-names: true + + st,max-clk-rate-hz: + description: + Allow to specify desired max clock rate used by analog circuitry. + + vdda-supply: + description: Phandle to the vdda input analog voltage. + + vref-supply: + description: Phandle to the vref input analog reference voltage. + + booster-supply: + description: + Phandle to the embedded booster regulator that can be used to supply ADC + analog input switches on stm32h7 and stm32mp1. + + vdd-supply: + description: + Phandle to the vdd input voltage. It can be used to supply ADC analog + input switches on stm32mp1. + + st,syscfg: + description: + Phandle to system configuration controller. It can be used to control the + analog circuitry on stm32mp1. + allOf: + - $ref: "/schemas/types.yaml#/definitions/phandle-array" + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +allOf: + - if: + properties: + compatible: + contains: + const: st,stm32f4-adc-core + + then: + properties: + clocks: + maxItems: 1 + + clock-names: + const: adc + + interrupts: + items: + - description: interrupt line common for all ADCs + + st,max-clk-rate-hz: + minimum: 600000 + maximum: 36000000 + default: 36000000 + + booster-supply: false + + vdd-supply: false + + st,syscfg: false + + - if: + properties: + compatible: + contains: + const: st,stm32h7-adc-core + + then: + properties: + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + items: + - const: bus + - const: adc + minItems: 1 + maxItems: 2 + + interrupts: + items: + - description: interrupt line common for all ADCs + + st,max-clk-rate-hz: + minimum: 120000 + maximum: 36000000 + default: 36000000 + + vdd-supply: false + + st,syscfg: false + + - if: + properties: + compatible: + contains: + const: st,stm32mp1-adc-core + + then: + properties: + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + items: + - const: bus + - const: adc + minItems: 1 + maxItems: 2 + + interrupts: + items: + - description: interrupt line for ADC1 + - description: interrupt line for ADC2 + + st,max-clk-rate-hz: + minimum: 120000 + maximum: 36000000 + default: 36000000 + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - vdda-supply + - vref-supply + - interrupt-controller + - '#interrupt-cells' + - '#address-cells' + - '#size-cells' + +patternProperties: + "^adc@[0-9]+$": + type: object + description: + An ADC block node should contain at least one subnode, representing an + ADC instance available on the machine. + + properties: + compatible: + enum: + - st,stm32f4-adc + - st,stm32h7-adc + - st,stm32mp1-adc + + reg: + description: | + Offset of ADC instance in ADC block. Valid values are: + - 0x0: ADC1 + - 0x100: ADC2 + - 0x200: ADC3 (stm32f4 only) + maxItems: 1 + + '#io-channel-cells': + const: 1 + + interrupts: + description: | + IRQ Line for the ADC instance. Valid values are: + - 0 for adc@0 + - 1 for adc@100 + - 2 for adc@200 (stm32f4 only) + maxItems: 1 + + clocks: + description: + Input clock private to this ADC instance. It's required only on + stm32f4, that has per instance clock input for registers access. + maxItems: 1 + + dmas: + description: RX DMA Channel + maxItems: 1 + + dma-names: + const: rx + + assigned-resolution-bits: + description: | + Resolution (bits) to use for conversions: + - can be 6, 8, 10 or 12 on stm32f4 + - can be 8, 10, 12, 14 or 16 on stm32h7 and stm32mp1 + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + + st,adc-channels: + description: | + List of single-ended channels muxed for this ADC. It can have up to: + - 16 channels, numbered from 0 to 15 (for in0..in15) on stm32f4 + - 20 channels, numbered from 0 to 19 (for in0..in19) on stm32h7 and + stm32mp1. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + + st,adc-diff-channels: + description: | + List of differential channels muxed for this ADC. Some channels can + be configured as differential instead of single-ended on stm32h7 and + on stm32mp1. Positive and negative inputs pairs are listed: + , ,... vinp and vinn are numbered from 0 to 19. + + Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is + required. Both properties can be used together. Some channels can be + used as single-ended and some other ones as differential (mixed). But + channels can't be configured both as single-ended and differential. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-matrix + - items: + items: + - description: | + "vinp" indicates positive input number + minimum: 0 + maximum: 19 + - description: | + "vinn" indicates negative input number + minimum: 0 + maximum: 19 + + st,min-sample-time-nsecs: + description: + Minimum sampling time in nanoseconds. Depending on hardware (board) + e.g. high/low analog input source impedance, fine tune of ADC + sampling time may be recommended. This can be either one value or an + array that matches "st,adc-channels" and/or "st,adc-diff-channels" + list, to set sample time resp. for all channels, or independently for + each channel. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32-array + + allOf: + - if: + properties: + compatible: + contains: + const: st,stm32f4-adc + + then: + properties: + reg: + enum: + - 0x0 + - 0x100 + - 0x200 + + interrupts: + minimum: 0 + maximum: 2 + + assigned-resolution-bits: + enum: [6, 8, 10, 12] + default: 12 + + st,adc-channels: + minItems: 1 + maxItems: 16 + items: + minimum: 0 + maximum: 15 + + st,adc-diff-channels: false + + st,min-sample-time-nsecs: + minItems: 1 + maxItems: 16 + items: + minimum: 80 + + required: + - clocks + + - if: + properties: + compatible: + contains: + enum: + - st,stm32h7-adc + - st,stm32mp1-adc + + then: + properties: + reg: + enum: + - 0x0 + - 0x100 + + interrupts: + minimum: 0 + maximum: 1 + + assigned-resolution-bits: + enum: [8, 10, 12, 14, 16] + default: 16 + + st,adc-channels: + minItems: 1 + maxItems: 20 + items: + minimum: 0 + maximum: 19 + + st,min-sample-time-nsecs: + minItems: 1 + maxItems: 20 + items: + minimum: 40 + + additionalProperties: false + + anyOf: + - required: + - st,adc-channels + - required: + - st,adc-diff-channels + + required: + - compatible + - reg + - interrupts + - '#io-channel-cells' + +examples: + - | + // Example 1: with stm32f429, ADC1, single-ended channel 8 + adc123: adc@40012000 { + compatible = "st,stm32f4-adc-core"; + reg = <0x40012000 0x400>; + interrupts = <18>; + clocks = <&rcc 0 168>; + clock-names = "adc"; + st,max-clk-rate-hz = <36000000>; + vdda-supply = <&vdda>; + vref-supply = <&vref>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + adc@0 { + compatible = "st,stm32f4-adc"; + #io-channel-cells = <1>; + reg = <0x0>; + clocks = <&rcc 0 168>; + interrupt-parent = <&adc123>; + interrupts = <0>; + st,adc-channels = <8>; + dmas = <&dma2 0 0 0x400 0x0>; + dma-names = "rx"; + assigned-resolution-bits = <8>; + }; + // ... + // other adc child nodes follow... + }; + + - | + // Example 2: with stm32mp157c to setup ADC1 with: + // - channels 0 & 1 as single-ended + // - channels 2 & 3 as differential (with resp. 6 & 7 negative inputs) + #include + #include + adc12: adc@48003000 { + compatible = "st,stm32mp1-adc-core"; + reg = <0x48003000 0x400>; + interrupts = , + ; + clocks = <&rcc ADC12>, <&rcc ADC12_K>; + clock-names = "bus", "adc"; + booster-supply = <&booster>; + vdd-supply = <&vdd>; + vdda-supply = <&vdda>; + vref-supply = <&vref>; + st,syscfg = <&syscfg>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + adc@0 { + compatible = "st,stm32mp1-adc"; + #io-channel-cells = <1>; + reg = <0x0>; + interrupt-parent = <&adc12>; + interrupts = <0>; + st,adc-channels = <0 1>; + st,adc-diff-channels = <2 6>, <3 7>; + st,min-sample-time-nsecs = <5000>; + dmas = <&dmamux1 9 0x400 0x05>; + dma-names = "rx"; + }; + // ... + // other adc child node follow... + }; + +... From 357528156207bbd21df89a287ef98eccd5fb64c3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 20 Jan 2020 11:25:50 +0200 Subject: [PATCH 0211/2231] iio: accel: st_accel: Use st_sensors_dev_name_probe() Use st_sensors_dev_name_probe() instead open coded variant. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_i2c.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 633955d764cc..8c489312f668 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -147,12 +147,9 @@ static int st_accel_i2c_probe(struct i2c_client *client) const struct st_sensor_settings *settings; struct st_sensor_data *adata; struct iio_dev *indio_dev; - const char *match; int ret; - match = device_get_match_data(&client->dev); - if (match) - strlcpy(client->name, match, sizeof(client->name)); + st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name)); settings = st_accel_get_settings(client->name); if (!settings) { From 5de6570b8abb5c29be52d119eb5f653df15c7338 Mon Sep 17 00:00:00 2001 From: JieunKim Date: Mon, 27 Jan 2020 21:03:59 +0900 Subject: [PATCH 0212/2231] iio: imu: st_lsm6dsx: Fix mismatched comments odr to odr_table gain to fs_table 'gain' is actually in 'st_lsm6dsx_fs' structure of 'fs_table' Signed-off-by: JieunKim Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 9c3486a8134f..f2113a63721a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -230,8 +230,8 @@ enum st_lsm6dsx_ext_sensor_id { * @i2c_addr: I2c slave address list. * @wai: Wai address info. * @id: external sensor id. - * @odr: Output data rate of the sensor [Hz]. - * @gain: Configured sensor sensitivity. + * @odr_table: Output data rate of the sensor [Hz]. + * @fs_table: Configured sensor sensitivity table depending on full scale. * @temp_comp: Temperature compensation register info (addr + mask). * @pwr_table: Power on register info (addr + mask). * @off_canc: Offset cancellation register info (addr + mask). From a2ff6e0347a9f1cbf930a1b121027c950c68c4ea Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 26 Jan 2020 16:05:47 +0100 Subject: [PATCH 0213/2231] iio: light: Add DT bindings for GP2AP002 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds device tree bindings for the GP2AP002 light and proximity sensor. As with other early proximity sensors (~2010) the light sensor and proximity sensors were combined into a single component. Cc: Stephan Gerhold Cc: Minkyu Kang Cc: Paweł Chmiel Cc: Jonathan Bakker Cc: Oskar Andero Cc: Dmitry Torokhov Cc: devicetree@vger.kernel.org Reviewed-by: Rob Herring Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- .../bindings/iio/light/sharp,gp2ap002.yaml | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml diff --git a/Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml b/Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml new file mode 100644 index 000000000000..12aa16f24772 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/sharp,gp2ap002.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sharp GP2AP002A00F and GP2AP002S00F proximity and ambient light sensors + +maintainers: + - Linus Walleij + +description: | + Proximity and ambient light sensor with IR LED for the proximity + sensing and an analog output for light intensity. The ambient light + sensor output is not available on the GP2AP002S00F variant. + +properties: + compatible: + enum: + - sharp,gp2ap002a00f + - sharp,gp2ap002s00f + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: an interrupt for proximity, usually a GPIO line + + vdd-supply: + description: VDD power supply a phandle to a regulator + + vio-supply: + description: VIO power supply a phandle to a regulator + + io-channels: + maxItems: 1 + description: ALSOUT ADC channel to read the ambient light + + io-channel-names: + const: alsout + + sharp,proximity-far-hysteresis: + $ref: /schemas/types.yaml#/definitions/uint8 + description: | + Hysteresis setting for "far" object detection, this setting is + device-unique and adjust the optical setting for proximity detection + of a "far away" object in front of the sensor. + + sharp,proximity-close-hysteresis: + $ref: /schemas/types.yaml#/definitions/uint8 + description: | + Hysteresis setting for "close" object detection, this setting is + device-unique and adjust the optical setting for proximity detection + of a "close" object in front of the sensor. + +required: + - compatible + - reg + - interrupts + - sharp,proximity-far-hysteresis + - sharp,proximity-close-hysteresis + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@44 { + compatible = "sharp,gp2ap002a00f"; + reg = <0x44>; + interrupts = <18 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&vdd_regulator>; + vio-supply = <&vio_regulator>; + io-channels = <&adc_channel>; + io-channel-names = "alsout"; + sharp,proximity-far-hysteresis = /bits/ 8 <0x2f>; + sharp,proximity-close-hysteresis = /bits/ 8 <0x0f>; + }; + }; + +... From 97d642e23037c5545266f9564c9b81e6db81b122 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 26 Jan 2020 16:05:48 +0100 Subject: [PATCH 0214/2231] iio: light: Add a driver for Sharp GP2AP002x00F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver handles two different Sharp sensors that have been proposed for merging to the mainline kernel over the years, and already has a limited proximity-only driver in the input subsystem. These components are completely different from the confusingly similarly named Sharp GP2AP020A00F, for which we have a driver in drivers/iio/light/gp2ap020a00f.c The two components GP2AP002A00F and GP2AP002S00F are distinctively different but similar: they share the same set of registers but differ slightly in the I2C protocol. Instead of the approach by the previous input driver, we create a combined IIO proximity and light sensor driver. The plan is to merge this driver and delete the input driver. The pieces for the driver are picked all over the place after researching and grepping through a few different vendor trees and driver submissions. We merge it under the light sensors because: - It has similarities with the Capella CM3605 light sensor and proximity driver which is there. - It is related to the GP2AP020A00F driver which is also there. This driver was tested with the Samsung GT-S7710 mobile phone which has the GP2AP002S00F proximity sensor mounted. The suspend/resume cycle will disable the interrupt from the sensor. If someone would desire to use this interrupt for wakeup, the driver will need modifications. Cc: Stephan Gerhold Cc: Minkyu Kang Cc: Paweł Chmiel Cc: Jonathan Bakker Cc: Oskar Andero Cc: Dmitry Torokhov Tested-by: Jonathan Bakker Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 + drivers/iio/light/Kconfig | 11 + drivers/iio/light/Makefile | 1 + drivers/iio/light/gp2ap002.c | 784 +++++++++++++++++++++++++++++++++++ 4 files changed, 804 insertions(+) create mode 100644 drivers/iio/light/gp2ap002.c diff --git a/MAINTAINERS b/MAINTAINERS index 9a4c715d1e50..d8ffbb4bf920 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15060,6 +15060,14 @@ W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: net/smc/ +SHARP GP2AP002A00F/GP2AP002S00F SENSOR DRIVER +M: Linus Walleij +L: linux-iio@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git +S: Maintained +F: drivers/iio/light/gp2ap002.c +F: Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml + SHARP RJ54N1CB0C SENSOR DRIVER M: Jacopo Mondi L: linux-media@vger.kernel.org diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 9968f982fbc7..307412c79c53 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -159,6 +159,17 @@ config IIO_CROS_EC_LIGHT_PROX To compile this driver as a module, choose M here: the module will be called cros_ec_light_prox. +config GP2AP002 + tristate "Sharp GP2AP002 Proximity/ALS sensor" + depends on I2C + select REGMAP + help + Say Y here if you have a Sharp GP2AP002 proximity/ALS combo-chip + hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called gp2ap002. + config GP2AP020A00F tristate "Sharp GP2AP020A00F Proximity/ALS sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index c98d1cefb861..264a76039006 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_CM3323) += cm3323.o obj-$(CONFIG_CM3605) += cm3605.o obj-$(CONFIG_CM36651) += cm36651.o obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o +obj-$(CONFIG_GP2AP002) += gp2ap002.o obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c new file mode 100644 index 000000000000..433907619268 --- /dev/null +++ b/drivers/iio/light/gp2ap002.c @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * These are the two Sharp GP2AP002 variants supported by this driver: + * GP2AP002A00F Ambient Light and Proximity Sensor + * GP2AP002S00F Proximity Sensor + * + * Copyright (C) 2020 Linaro Ltd. + * Author: Linus Walleij + * + * Based partly on the code in Sony Ericssons GP2AP00200F driver by + * Courtney Cavin and Oskar Andero in drivers/input/misc/gp2ap002a00f.c + * Based partly on a Samsung misc driver submitted by + * Donggeun Kim & Minkyu Kang in 2011: + * https://lore.kernel.org/lkml/1315556546-7445-1-git-send-email-dg77.kim@samsung.com/ + * Based partly on a submission by + * Jonathan Bakker and Paweł Chmiel in january 2019: + * https://lore.kernel.org/linux-input/20190125175045.22576-1-pawel.mikolaj.chmiel@gmail.com/ + * Based partly on code from the Samsung GT-S7710 by + * Based partly on the code in LG Electronics GP2AP00200F driver by + * Kenobi Lee and EunYoung Cho + */ +#include +#include +#include +#include +#include +#include +#include /* To get our ADC channel */ +#include /* To deal with our ADC channel */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define GP2AP002_PROX_CHANNEL 0 +#define GP2AP002_ALS_CHANNEL 1 + +/* ------------------------------------------------------------------------ */ +/* ADDRESS SYMBOL DATA Init R/W */ +/* D7 D6 D5 D4 D3 D2 D1 D0 */ +/* ------------------------------------------------------------------------ */ +/* 0 PROX X X X X X X X VO H'00 R */ +/* 1 GAIN X X X X LED0 X X X H'00 W */ +/* 2 HYS HYSD HYSC1 HYSC0 X HYSF3 HYSF2 HYSF1 HYSF0 H'00 W */ +/* 3 CYCLE X X CYCL2 CYCL1 CYCL0 OSC2 X X H'00 W */ +/* 4 OPMOD X X X ASD X X VCON SSD H'00 W */ +/* 6 CON X X X OCON1 OCON0 X X X H'00 W */ +/* ------------------------------------------------------------------------ */ +/* VO :Proximity sensing result(0: no detection, 1: detection) */ +/* LED0 :Select switch for LED driver's On-registence(0:2x higher, 1:normal)*/ +/* HYSD/HYSF :Adjusts the receiver sensitivity */ +/* OSC :Select switch internal clocl frequency hoppling(0:effective) */ +/* CYCL :Determine the detection cycle(typically 8ms, up to 128x) */ +/* SSD :Software Shutdown function(0:shutdown, 1:operating) */ +/* VCON :VOUT output method control(0:normal, 1:interrupt) */ +/* ASD :Select switch for analog sleep function(0:ineffective, 1:effective)*/ +/* OCON :Select switch for enabling/disabling VOUT (00:enable, 11:disable) */ + +#define GP2AP002_PROX 0x00 +#define GP2AP002_GAIN 0x01 +#define GP2AP002_HYS 0x02 +#define GP2AP002_CYCLE 0x03 +#define GP2AP002_OPMOD 0x04 +#define GP2AP002_CON 0x06 + +#define GP2AP002_PROX_VO_DETECT BIT(0) + +/* Setting this bit to 0 means 2x higher LED resistance */ +#define GP2AP002_GAIN_LED_NORMAL BIT(3) + +/* + * These bits adjusts the proximity sensitivity, determining characteristics + * of the detection distance and its hysteresis. + */ +#define GP2AP002_HYS_HYSD_SHIFT 7 +#define GP2AP002_HYS_HYSD_MASK BIT(7) +#define GP2AP002_HYS_HYSC_SHIFT 5 +#define GP2AP002_HYS_HYSC_MASK GENMASK(6, 5) +#define GP2AP002_HYS_HYSF_SHIFT 0 +#define GP2AP002_HYS_HYSF_MASK GENMASK(3, 0) +#define GP2AP002_HYS_MASK (GP2AP002_HYS_HYSD_MASK | \ + GP2AP002_HYS_HYSC_MASK | \ + GP2AP002_HYS_HYSF_MASK) + +/* + * These values determine the detection cycle response time + * 0: 8ms, 1: 16ms, 2: 32ms, 3: 64ms, 4: 128ms, + * 5: 256ms, 6: 512ms, 7: 1024ms + */ +#define GP2AP002_CYCLE_CYCL_SHIFT 3 +#define GP2AP002_CYCLE_CYCL_MASK GENMASK(5, 3) + +/* + * Select switch for internal clock frequency hopping + * 0: effective, + * 1: ineffective + */ +#define GP2AP002_CYCLE_OSC_EFFECTIVE 0 +#define GP2AP002_CYCLE_OSC_INEFFECTIVE BIT(2) +#define GP2AP002_CYCLE_OSC_MASK BIT(2) + +/* Analog sleep effective */ +#define GP2AP002_OPMOD_ASD BIT(4) +/* Enable chip */ +#define GP2AP002_OPMOD_SSD_OPERATING BIT(0) +/* IRQ mode */ +#define GP2AP002_OPMOD_VCON_IRQ BIT(1) +#define GP2AP002_OPMOD_MASK (BIT(0) | BIT(1) | BIT(4)) + +/* + * Select switch for enabling/disabling Vout pin + * 0: enable + * 2: force to go Low + * 3: force to go High + */ +#define GP2AP002_CON_OCON_SHIFT 3 +#define GP2AP002_CON_OCON_ENABLE (0x0 << GP2AP002_CON_OCON_SHIFT) +#define GP2AP002_CON_OCON_LOW (0x2 << GP2AP002_CON_OCON_SHIFT) +#define GP2AP002_CON_OCON_HIGH (0x3 << GP2AP002_CON_OCON_SHIFT) +#define GP2AP002_CON_OCON_MASK (0x3 << GP2AP002_CON_OCON_SHIFT) + +/** + * struct gp2ap002 - GP2AP002 state + * @map: regmap pointer for the i2c regmap + * @dev: pointer to parent device + * @vdd: regulator controlling VDD + * @vio: regulator controlling VIO + * @alsout: IIO ADC channel to convert the ALSOUT signal + * @hys_far: hysteresis control from device tree + * @hys_close: hysteresis control from device tree + * @is_gp2ap002s00f: this is the GP2AP002F variant of the chip + * @irq: the IRQ line used by this device + * @enabled: we cannot read the status of the hardware so we need to + * keep track of whether the event is enabled using this state variable + */ +struct gp2ap002 { + struct regmap *map; + struct device *dev; + struct regulator *vdd; + struct regulator *vio; + struct iio_channel *alsout; + u8 hys_far; + u8 hys_close; + bool is_gp2ap002s00f; + int irq; + bool enabled; +}; + +static irqreturn_t gp2ap002_prox_irq(int irq, void *d) +{ + struct iio_dev *indio_dev = d; + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + u64 ev; + int val; + int ret; + + ret = regmap_read(gp2ap002->map, GP2AP002_PROX, &val); + if (ret) { + dev_err(gp2ap002->dev, "error reading proximity\n"); + goto err_retrig; + } + + if (val & GP2AP002_PROX_VO_DETECT) { + /* Close */ + dev_dbg(gp2ap002->dev, "close\n"); + ret = regmap_write(gp2ap002->map, GP2AP002_HYS, + gp2ap002->hys_far); + if (ret) + dev_err(gp2ap002->dev, + "error setting up proximity hysteresis\n"); + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING); + } else { + /* Far */ + dev_dbg(gp2ap002->dev, "far\n"); + ret = regmap_write(gp2ap002->map, GP2AP002_HYS, + gp2ap002->hys_close); + if (ret) + dev_err(gp2ap002->dev, + "error setting up proximity hysteresis\n"); + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING); + } + iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev)); + + /* + * After changing hysteresis, we need to wait for one detection + * cycle to see if anything changed, or we will just trigger the + * previous interrupt again. A detection cycle depends on the CYCLE + * register, we are hard-coding ~8 ms in probe() so wait some more + * than this, 20-30 ms. + */ + usleep_range(20000, 30000); + +err_retrig: + ret = regmap_write(gp2ap002->map, GP2AP002_CON, + GP2AP002_CON_OCON_ENABLE); + if (ret) + dev_err(gp2ap002->dev, "error setting up VOUT control\n"); + + return IRQ_HANDLED; +} + +struct gp2ap002_illuminance { + unsigned int curr; + unsigned int lux; +}; + +/* + * This array maps current and lux. + * + * Ambient light sensing range is 3 to 55000 lux. + * + * This mapping is based on the following formula. + * illuminance = 10 ^ (current / 10) + */ +static const struct gp2ap002_illuminance gp2ap002_illuminance_table[] = { + { .curr = 5, .lux = 3 }, + { .curr = 6, .lux = 4 }, + { .curr = 7, .lux = 5 }, + { .curr = 8, .lux = 6 }, + { .curr = 9, .lux = 8 }, + { .curr = 10, .lux = 10 }, + { .curr = 11, .lux = 12 }, + { .curr = 12, .lux = 16 }, + { .curr = 13, .lux = 20 }, + { .curr = 14, .lux = 25 }, + { .curr = 15, .lux = 32 }, + { .curr = 16, .lux = 40 }, + { .curr = 17, .lux = 50 }, + { .curr = 18, .lux = 63 }, + { .curr = 19, .lux = 79 }, + { .curr = 20, .lux = 100 }, + { .curr = 21, .lux = 126 }, + { .curr = 22, .lux = 158 }, + { .curr = 23, .lux = 200 }, + { .curr = 24, .lux = 251 }, + { .curr = 25, .lux = 316 }, + { .curr = 26, .lux = 398 }, + { .curr = 27, .lux = 501 }, + { .curr = 28, .lux = 631 }, + { .curr = 29, .lux = 794 }, + { .curr = 30, .lux = 1000 }, + { .curr = 31, .lux = 1259 }, + { .curr = 32, .lux = 1585 }, + { .curr = 33, .lux = 1995 }, + { .curr = 34, .lux = 2512 }, + { .curr = 35, .lux = 3162 }, + { .curr = 36, .lux = 3981 }, + { .curr = 37, .lux = 5012 }, + { .curr = 38, .lux = 6310 }, + { .curr = 39, .lux = 7943 }, + { .curr = 40, .lux = 10000 }, + { .curr = 41, .lux = 12589 }, + { .curr = 42, .lux = 15849 }, + { .curr = 43, .lux = 19953 }, + { .curr = 44, .lux = 25119 }, + { .curr = 45, .lux = 31623 }, + { .curr = 46, .lux = 39811 }, + { .curr = 47, .lux = 50119 }, +}; + +static int gp2ap002_get_lux(struct gp2ap002 *gp2ap002) +{ + const struct gp2ap002_illuminance *ill1; + const struct gp2ap002_illuminance *ill2; + int ret, res; + int i; + + ret = iio_read_channel_processed(gp2ap002->alsout, &res); + if (ret < 0) + return ret; + + dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res); + + ill1 = &gp2ap002_illuminance_table[0]; + if (res < ill1->curr) { + dev_dbg(gp2ap002->dev, "total darkness\n"); + return 0; + } + for (i = 0; i < ARRAY_SIZE(gp2ap002_illuminance_table) - 1; i++) { + ill1 = &gp2ap002_illuminance_table[i]; + ill2 = &gp2ap002_illuminance_table[i + 1]; + + if (res > ill2->curr) + continue; + if ((res <= ill1->curr) && (res >= ill2->curr)) + break; + } + if (res > ill2->curr) { + dev_info_once(gp2ap002->dev, "max current overflow\n"); + return ill2->curr; + } + /* Interpolate and return */ + dev_dbg(gp2ap002->dev, "interpolate index %d and %d\n", i, i + 1); + /* How many steps along the curve */ + i = res - ill1->curr; /* x - x0 */ + /* Linear interpolation */ + return ill1->lux + i * + ((ill2->lux - ill1->lux) / (ill2->curr - ill1->curr)); +} + +static int gp2ap002_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_LIGHT: + ret = gp2ap002_get_lux(gp2ap002); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int gp2ap002_init(struct gp2ap002 *gp2ap002) +{ + int ret; + + /* Set up the IR LED resistance */ + ret = regmap_write(gp2ap002->map, GP2AP002_GAIN, + GP2AP002_GAIN_LED_NORMAL); + if (ret) { + dev_err(gp2ap002->dev, "error setting up LED gain\n"); + return ret; + } + ret = regmap_write(gp2ap002->map, GP2AP002_HYS, gp2ap002->hys_far); + if (ret) { + dev_err(gp2ap002->dev, + "error setting up proximity hysteresis\n"); + return ret; + } + + /* Disable internal frequency hopping */ + ret = regmap_write(gp2ap002->map, GP2AP002_CYCLE, + GP2AP002_CYCLE_OSC_INEFFECTIVE); + if (ret) { + dev_err(gp2ap002->dev, + "error setting up internal frequency hopping\n"); + return ret; + } + + /* Enable chip and IRQ, disable analog sleep */ + ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, + GP2AP002_OPMOD_SSD_OPERATING | + GP2AP002_OPMOD_VCON_IRQ); + if (ret) { + dev_err(gp2ap002->dev, "error setting up operation mode\n"); + return ret; + } + + /* Interrupt on VOUT enabled */ + ret = regmap_write(gp2ap002->map, GP2AP002_CON, + GP2AP002_CON_OCON_ENABLE); + if (ret) + dev_err(gp2ap002->dev, "error setting up VOUT control\n"); + + return ret; +} + +static int gp2ap002_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + + /* + * We just keep track of this internally, as it is not possible to + * query the hardware. + */ + return gp2ap002->enabled; +} + +static int gp2ap002_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + + if (state) { + /* + * This will bring the regulators up (unless they are on + * already) and reintialize the sensor by using runtime_pm + * callbacks. + */ + pm_runtime_get_sync(gp2ap002->dev); + gp2ap002->enabled = true; + } else { + pm_runtime_mark_last_busy(gp2ap002->dev); + pm_runtime_put_autosuspend(gp2ap002->dev); + gp2ap002->enabled = false; + } + + return 0; +} + +static const struct iio_info gp2ap002_info = { + .read_raw = gp2ap002_read_raw, + .read_event_config = gp2ap002_read_event_config, + .write_event_config = gp2ap002_write_event_config, +}; + +static const struct iio_event_spec gp2ap002_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec gp2ap002_channels[] = { + { + .type = IIO_PROXIMITY, + .event_spec = gp2ap002_events, + .num_event_specs = ARRAY_SIZE(gp2ap002_events), + }, + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .channel = GP2AP002_ALS_CHANNEL, + }, +}; + +/* + * We need a special regmap because this hardware expects to + * write single bytes to registers but read a 16bit word on some + * variants and discard the lower 8 bits so combine + * i2c_smbus_read_word_data() with i2c_smbus_write_byte_data() + * selectively like this. + */ +static int gp2ap002_regmap_i2c_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_read_word_data(i2c, reg); + if (ret < 0) + return ret; + + *val = (ret >> 8) & 0xFF; + + return 0; +} + +static int gp2ap002_regmap_i2c_write(void *context, unsigned int reg, + unsigned int val) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(i2c, reg, val); +} + +static struct regmap_bus gp2ap002_regmap_bus = { + .reg_read = gp2ap002_regmap_i2c_read, + .reg_write = gp2ap002_regmap_i2c_write, +}; + +static int gp2ap002_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct gp2ap002 *gp2ap002; + struct iio_dev *indio_dev; + struct device *dev = &client->dev; + enum iio_chan_type ch_type; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = GP2AP002_CON, + }; + struct regmap *regmap; + int num_chan; + const char *compat; + u8 val; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gp2ap002)); + if (!indio_dev) + return -ENOMEM; + i2c_set_clientdata(client, indio_dev); + + gp2ap002 = iio_priv(indio_dev); + gp2ap002->dev = dev; + + /* + * Check the device compatible like this makes it possible to use + * ACPI PRP0001 for registering the sensor using device tree + * properties. + */ + ret = device_property_read_string(dev, "compatible", &compat); + if (ret) { + dev_err(dev, "cannot check compatible\n"); + return ret; + } + gp2ap002->is_gp2ap002s00f = !strcmp(compat, "sharp,gp2ap002s00f"); + + regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + gp2ap002->map = regmap; + + /* + * The hysteresis settings are coded into the device tree as values + * to be written into the hysteresis register. The datasheet defines + * modes "A", "B1" and "B2" with fixed values to be use but vendor + * code trees for actual devices are tweaking these values and refer to + * modes named things like "B1.5". To be able to support any devices, + * we allow passing an arbitrary hysteresis setting for "near" and + * "far". + */ + + /* Check the device tree for the IR LED hysteresis */ + ret = device_property_read_u8(dev, "sharp,proximity-far-hysteresis", + &val); + if (ret) { + dev_err(dev, "failed to obtain proximity far setting\n"); + return ret; + } + dev_dbg(dev, "proximity far setting %02x\n", val); + gp2ap002->hys_far = val; + + ret = device_property_read_u8(dev, "sharp,proximity-close-hysteresis", + &val); + if (ret) { + dev_err(dev, "failed to obtain proximity close setting\n"); + return ret; + } + dev_dbg(dev, "proximity close setting %02x\n", val); + gp2ap002->hys_close = val; + + /* The GP2AP002A00F has a light sensor too */ + if (!gp2ap002->is_gp2ap002s00f) { + gp2ap002->alsout = devm_iio_channel_get(dev, "alsout"); + if (IS_ERR(gp2ap002->alsout)) { + if (PTR_ERR(gp2ap002->alsout) == -ENODEV) { + dev_err(dev, "no ADC, deferring...\n"); + return -EPROBE_DEFER; + } + dev_err(dev, "failed to get ALSOUT ADC channel\n"); + return PTR_ERR(gp2ap002->alsout); + } + ret = iio_get_channel_type(gp2ap002->alsout, &ch_type); + if (ret < 0) + return ret; + if (ch_type != IIO_CURRENT) { + dev_err(dev, + "wrong type of IIO channel specified for ALSOUT\n"); + return -EINVAL; + } + } + + gp2ap002->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(gp2ap002->vdd)) { + dev_err(dev, "failed to get VDD regulator\n"); + return PTR_ERR(gp2ap002->vdd); + } + gp2ap002->vio = devm_regulator_get(dev, "vio"); + if (IS_ERR(gp2ap002->vio)) { + dev_err(dev, "failed to get VIO regulator\n"); + return PTR_ERR(gp2ap002->vio); + } + + /* Operating voltage 2.4V .. 3.6V according to datasheet */ + ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000); + if (ret) { + dev_err(dev, "failed to sett VDD voltage\n"); + return ret; + } + + /* VIO should be between 1.65V and VDD */ + ret = regulator_get_voltage(gp2ap002->vdd); + if (ret < 0) { + dev_err(dev, "failed to get VDD voltage\n"); + return ret; + } + ret = regulator_set_voltage(gp2ap002->vio, 1650000, ret); + if (ret) { + dev_err(dev, "failed to set VIO voltage\n"); + return ret; + } + + ret = regulator_enable(gp2ap002->vdd); + if (ret) { + dev_err(dev, "failed to enable VDD regulator\n"); + return ret; + } + ret = regulator_enable(gp2ap002->vio); + if (ret) { + dev_err(dev, "failed to enable VIO regulator\n"); + goto out_disable_vdd; + } + + msleep(20); + + /* + * Initialize the device and signal to runtime PM that now we are + * definately up and using power. + */ + ret = gp2ap002_init(gp2ap002); + if (ret) { + dev_err(dev, "initialization failed\n"); + goto out_disable_vio; + } + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + gp2ap002->enabled = false; + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + gp2ap002_prox_irq, IRQF_ONESHOT, + "gp2ap002", indio_dev); + if (ret) { + dev_err(dev, "unable to request IRQ\n"); + goto out_disable_vio; + } + gp2ap002->irq = client->irq; + + /* + * As the device takes 20 ms + regulator delay to come up with a fresh + * measurement after power-on, do not shut it down unnecessarily. + * Set autosuspend to a one second. + */ + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + indio_dev->dev.parent = dev; + indio_dev->info = &gp2ap002_info; + indio_dev->name = "gp2ap002"; + indio_dev->channels = gp2ap002_channels; + /* Skip light channel for the proximity-only sensor */ + num_chan = ARRAY_SIZE(gp2ap002_channels); + if (gp2ap002->is_gp2ap002s00f) + num_chan--; + indio_dev->num_channels = num_chan; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(indio_dev); + if (ret) + goto out_disable_pm; + dev_dbg(dev, "Sharp GP2AP002 probed successfully\n"); + + return 0; + +out_disable_pm: + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); +out_disable_vio: + regulator_disable(gp2ap002->vio); +out_disable_vdd: + regulator_disable(gp2ap002->vdd); + return ret; +} + +static int gp2ap002_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + struct device *dev = &client->dev; + + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + iio_device_unregister(indio_dev); + regulator_disable(gp2ap002->vio); + regulator_disable(gp2ap002->vdd); + + return 0; +} + +static int __maybe_unused gp2ap002_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + int ret; + + /* Deactivate the IRQ */ + disable_irq(gp2ap002->irq); + + /* Disable chip and IRQ, everything off */ + ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00); + if (ret) { + dev_err(gp2ap002->dev, "error setting up operation mode\n"); + return ret; + } + /* + * As these regulators may be shared, at least we are now in + * sleep even if the regulators aren't really turned off. + */ + regulator_disable(gp2ap002->vio); + regulator_disable(gp2ap002->vdd); + + return 0; +} + +static int __maybe_unused gp2ap002_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(gp2ap002->vdd); + if (ret) { + dev_err(dev, "failed to enable VDD regulator in resume path\n"); + return ret; + } + ret = regulator_enable(gp2ap002->vio); + if (ret) { + dev_err(dev, "failed to enable VIO regulator in resume path\n"); + return ret; + } + + msleep(20); + + ret = gp2ap002_init(gp2ap002); + if (ret) { + dev_err(dev, "re-initialization failed\n"); + return ret; + } + + /* Re-activate the IRQ */ + enable_irq(gp2ap002->irq); + + return 0; +} + +static const struct dev_pm_ops gp2ap002_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(gp2ap002_runtime_suspend, + gp2ap002_runtime_resume, NULL) +}; + +static const struct i2c_device_id gp2ap002_id_table[] = { + { "gp2ap002", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, gp2ap002_id_table); + +static const struct of_device_id gp2ap002_of_match[] = { + { .compatible = "sharp,gp2ap002a00f" }, + { .compatible = "sharp,gp2ap002s00f" }, + { }, +}; +MODULE_DEVICE_TABLE(of, gp2ap002_of_match); + +static struct i2c_driver gp2ap002_driver = { + .driver = { + .name = "gp2ap002", + .of_match_table = gp2ap002_of_match, + .pm = &gp2ap002_dev_pm_ops, + }, + .probe = gp2ap002_probe, + .remove = gp2ap002_remove, + .id_table = gp2ap002_id_table, +}; +module_i2c_driver(gp2ap002_driver); + +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver"); +MODULE_LICENSE("GPL v2"); From ebf7a1126717a19fb638806d37dbdf27124a272d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 23 Jan 2020 09:19:54 +0000 Subject: [PATCH 0215/2231] iio: ad5755: fix spelling mistake "to" -> "too" and grammar plus formatting There is a spelling mistake and grammar mistake in a dev_err message. Fix it. Also include Joe Perches' additional suggestions around: * Missing newlines * Excess braces * Odd indentation * Some grammar Signed-off-by: Colin Ian King Reviewed-by: Alexandru Ardelean Suggested-by: Joe Perches # Additional cleanups etc. Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5755.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index b9175fb4c8ab..388ddd14bfd0 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -631,10 +631,9 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) } } - if (i == ARRAY_SIZE(ad5755_dcdc_freq_table)) { + if (i == ARRAY_SIZE(ad5755_dcdc_freq_table)) dev_err(dev, - "adi,dc-dc-freq out of range selecting 410kHz"); - } + "adi,dc-dc-freq out of range selecting 410kHz\n"); } pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V; @@ -645,17 +644,16 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) break; } } - if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table)) { + if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table)) dev_err(dev, - "adi,dc-dc-maxv out of range selecting 23V"); - } + "adi,dc-dc-maxv out of range selecting 23V\n"); } devnr = 0; for_each_child_of_node(np, pp) { if (devnr >= AD5755_NUM_CHANNELS) { dev_err(dev, - "There is to many channels defined in DT\n"); + "There are too many channels defined in DT\n"); goto error_out; } @@ -681,11 +679,10 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) break; } } - if (i == ARRAY_SIZE(ad5755_slew_rate_table)) { + if (i == ARRAY_SIZE(ad5755_slew_rate_table)) dev_err(dev, - "channel %d slew rate out of range selecting 64kHz", + "channel %d slew rate out of range selecting 64kHz\n", devnr); - } pdata->dac[devnr].slew.step_size = AD5755_SLEW_STEP_SIZE_1; for (i = 0; i < ARRAY_SIZE(ad5755_slew_step_table); i++) { @@ -695,11 +692,10 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) break; } } - if (i == ARRAY_SIZE(ad5755_slew_step_table)) { + if (i == ARRAY_SIZE(ad5755_slew_step_table)) dev_err(dev, - "channel %d slew step size out of range selecting 1 LSB", + "channel %d slew step size out of range selecting 1 LSB\n", devnr); - } } else { pdata->dac[devnr].slew.enable = false; pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k; From cef2760954cf4f8228d14af9bd0b77a021979278 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Wed, 22 Jan 2020 10:54:14 +0200 Subject: [PATCH 0216/2231] iio: adc: ad7124: add 3db filter This patch adds the LOW_PASS_FILTER_3DB_FREQUENCY attribute in iio_chan_spec for each channel. The used filters are sinc3 or sinc4. The filter type with the highest output data rate is used when setting a low pass frequency in the channel's sysfs. Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 69 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index d9915dc71d1e..f0206d819fda 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -70,6 +70,11 @@ /* AD7124_FILTER_X */ #define AD7124_FILTER_FS_MSK GENMASK(10, 0) #define AD7124_FILTER_FS(x) FIELD_PREP(AD7124_FILTER_FS_MSK, x) +#define AD7124_FILTER_TYPE_MSK GENMASK(23, 21) +#define AD7124_FILTER_TYPE_SEL(x) FIELD_PREP(AD7124_FILTER_TYPE_MSK, x) + +#define AD7124_SINC3_FILTER 2 +#define AD7124_SINC4_FILTER 0 enum ad7124_ids { ID_AD7124_4, @@ -119,6 +124,7 @@ struct ad7124_channel_config { unsigned int vref_mv; unsigned int pga_bits; unsigned int odr; + unsigned int filter_type; }; struct ad7124_state { @@ -138,7 +144,8 @@ static const struct iio_chan_spec ad7124_channel_template = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | - BIT(IIO_CHAN_INFO_SAMP_FREQ), + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), .scan_type = { .sign = 'u', .realbits = 24, @@ -281,6 +288,58 @@ static int ad7124_set_channel_gain(struct ad7124_state *st, return 0; } +static int ad7124_get_3db_filter_freq(struct ad7124_state *st, + unsigned int channel) +{ + unsigned int fadc; + + fadc = st->channel_config[channel].odr; + + switch (st->channel_config[channel].filter_type) { + case AD7124_SINC3_FILTER: + return DIV_ROUND_CLOSEST(fadc * 230, 1000); + case AD7124_SINC4_FILTER: + return DIV_ROUND_CLOSEST(fadc * 262, 1000); + default: + return -EINVAL; + } +} + +static int ad7124_set_3db_filter_freq(struct ad7124_state *st, + unsigned int channel, + unsigned int freq) +{ + unsigned int sinc4_3db_odr; + unsigned int sinc3_3db_odr; + unsigned int new_filter; + unsigned int new_odr; + + sinc4_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 230); + sinc3_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 262); + + if (sinc4_3db_odr > sinc3_3db_odr) { + new_filter = AD7124_SINC3_FILTER; + new_odr = sinc4_3db_odr; + } else { + new_filter = AD7124_SINC4_FILTER; + new_odr = sinc3_3db_odr; + } + + if (st->channel_config[channel].filter_type != new_filter) { + int ret; + + st->channel_config[channel].filter_type = new_filter; + ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel), + AD7124_FILTER_TYPE_MSK, + AD7124_FILTER_TYPE_SEL(new_filter), + 3); + if (ret < 0) + return ret; + } + + return ad7124_set_channel_odr(st, channel, new_odr); +} + static int ad7124_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) @@ -322,6 +381,9 @@ static int ad7124_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SAMP_FREQ: *val = st->channel_config[chan->address].odr; + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *val = ad7124_get_3db_filter_freq(st, chan->scan_index); return IIO_VAL_INT; default: return -EINVAL; @@ -355,6 +417,11 @@ static int ad7124_write_raw(struct iio_dev *indio_dev, gain = DIV_ROUND_CLOSEST(res, val2); return ad7124_set_channel_gain(st, chan->address, gain); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + if (val2 != 0) + return -EINVAL; + + return ad7124_set_3db_filter_freq(st, chan->address, val); default: return -EINVAL; } From 9960c70949d4356abed8747a20637e0946bb0bad Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 21 Jan 2020 16:17:57 +0000 Subject: [PATCH 0217/2231] iio: st_sensors: handle memory allocation failure to fix null pointer dereference A null pointer deference on pdata can occur if the allocation of pdata fails. Fix this by adding a null pointer check and handle the -ENOMEM failure in the caller. Addresses-Coverity: ("Dereference null return value") Fixes: 3ce85cc4fbb7 ("iio: st_sensors: get platform data from device tree") Signed-off-by: Colin Ian King Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index e051edbc43c1..0e35ff06f9af 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -328,6 +328,8 @@ static struct st_sensors_platform_data *st_sensors_dev_probe(struct device *dev, return NULL; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); if (!device_property_read_u32(dev, "st,drdy-int-pin", &val) && (val <= 2)) pdata->drdy_int_pin = (u8) val; else @@ -371,6 +373,8 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev, /* If OF/DT pdata exists, it will take precedence of anything else */ of_pdata = st_sensors_dev_probe(indio_dev->dev.parent, pdata); + if (IS_ERR(of_pdata)) + return PTR_ERR(of_pdata); if (of_pdata) pdata = of_pdata; From 66053d5d1b74be17b133775a4373888ba227e902 Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Mon, 20 Jan 2020 17:44:55 +0100 Subject: [PATCH 0218/2231] dt-bindings: devantech-srf04.yaml: add pm feature Add GPIO line and startup time for usage of power management Reviewed-by: Rob Herring Signed-off-by: Andreas Klinger Signed-off-by: Jonathan Cameron --- .../iio/proximity/devantech-srf04.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.yaml b/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.yaml index 4e80ea7c1475..8afbac24c34e 100644 --- a/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.yaml @@ -51,6 +51,24 @@ properties: the time between two interrupts is measured in the driver. maxItems: 1 + power-gpios: + description: + Definition of the GPIO for power management of connected peripheral + (output). + This GPIO can be used by the external hardware for power management. + When the device gets suspended it's switched off and when it resumes + it's switched on again. After some period of inactivity the driver + get suspended automatically (autosuspend feature). + maxItems: 1 + + startup-time-ms: + description: + This is the startup time the device needs after a resume to be up and + running. + minimum: 0 + maximum: 1000 + default: 100 + required: - compatible - trig-gpios From 2251157b335b46bb3ea36994301f906e32e2d2b0 Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Mon, 20 Jan 2020 17:45:41 +0100 Subject: [PATCH 0219/2231] iio: srf04: add power management feature Add suspend and resume operations for being used by optional power management. The suspend function is switching off an GPIO which can be used by the hardware to switch power off. The resume function is switching the GPIO on and sleeps an adjustable time to give the device a chance to be up and running. If activated the driver gets into autosuspend after some time of inactivity. Suggested-by: Franz Parzer Signed-off-by: Andreas Klinger Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/srf04.c | 96 ++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 01eb8cc63076..568b76e06385 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,7 @@ struct srf04_data { struct device *dev; struct gpio_desc *gpiod_trig; struct gpio_desc *gpiod_echo; + struct gpio_desc *gpiod_power; struct mutex lock; int irqnr; ktime_t ts_rising; @@ -63,6 +65,7 @@ struct srf04_data { struct completion rising; struct completion falling; const struct srf04_cfg *cfg; + int startup_time_ms; }; static const struct srf04_cfg srf04_cfg = { @@ -97,6 +100,9 @@ static int srf04_read(struct srf04_data *data) u64 dt_ns; u32 time_ns, distance_mm; + if (data->gpiod_power) + pm_runtime_get_sync(data->dev); + /* * just one read-echo-cycle can take place at a time * ==> lock against concurrent reading calls @@ -110,6 +116,11 @@ static int srf04_read(struct srf04_data *data) udelay(data->cfg->trigger_pulse_us); gpiod_set_value(data->gpiod_trig, 0); + if (data->gpiod_power) { + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + } + /* it should not take more than 20 ms until echo is rising */ ret = wait_for_completion_killable_timeout(&data->rising, HZ/50); if (ret < 0) { @@ -268,6 +279,22 @@ static int srf04_probe(struct platform_device *pdev) return PTR_ERR(data->gpiod_echo); } + data->gpiod_power = devm_gpiod_get_optional(dev, "power", + GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_power)) { + dev_err(dev, "failed to get power-gpios: err=%ld\n", + PTR_ERR(data->gpiod_power)); + return PTR_ERR(data->gpiod_power); + } + if (data->gpiod_power) { + + if (of_property_read_u32(dev->of_node, "startup-time-ms", + &data->startup_time_ms)) + data->startup_time_ms = 100; + dev_dbg(dev, "using power gpio: startup-time-ms=%d\n", + data->startup_time_ms); + } + if (gpiod_cansleep(data->gpiod_echo)) { dev_err(data->dev, "cansleep-GPIOs not supported\n"); return -ENODEV; @@ -296,14 +323,81 @@ static int srf04_probe(struct platform_device *pdev) indio_dev->channels = srf04_chan_spec; indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec); - return devm_iio_device_register(dev, indio_dev); + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(data->dev, "iio_device_register: %d\n", ret); + return ret; + } + + if (data->gpiod_power) { + pm_runtime_set_autosuspend_delay(data->dev, 1000); + pm_runtime_use_autosuspend(data->dev); + + ret = pm_runtime_set_active(data->dev); + if (ret) { + dev_err(data->dev, "pm_runtime_set_active: %d\n", ret); + iio_device_unregister(indio_dev); + } + + pm_runtime_enable(data->dev); + pm_runtime_idle(data->dev); + } + + return ret; } +static int srf04_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct srf04_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (data->gpiod_power) { + pm_runtime_disable(data->dev); + pm_runtime_set_suspended(data->dev); + } + + return 0; +} + +static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct srf04_data *data = iio_priv(indio_dev); + + gpiod_set_value(data->gpiod_power, 0); + + return 0; +} + +static int __maybe_unused srf04_pm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct srf04_data *data = iio_priv(indio_dev); + + gpiod_set_value(data->gpiod_power, 1); + msleep(data->startup_time_ms); + + return 0; +} + +static const struct dev_pm_ops srf04_pm_ops = { + SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend, + srf04_pm_runtime_resume, NULL) +}; + static struct platform_driver srf04_driver = { .probe = srf04_probe, + .remove = srf04_remove, .driver = { .name = "srf04-gpio", .of_match_table = of_srf04_match, + .pm = &srf04_pm_ops, }, }; From 3084ea9ea88906576d0bfc0d66cb1735045266c8 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 17:20:09 -0600 Subject: [PATCH 0220/2231] thunderbolt: icm: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 13e88109742e..fbbe32ca1e69 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -114,7 +114,7 @@ struct icm_notification { struct ep_name_entry { u8 len; u8 type; - u8 data[0]; + u8 data[]; }; #define EP_NAME_INTEL_VSS 0x10 From 7c805795307b40af50a45b7db44dd09ac1700947 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 23 Oct 2019 14:27:14 +0200 Subject: [PATCH 0221/2231] x86/entry: Remove _TIF_NOHZ from _TIF_WORK_SYSCALL_ENTRY Evaluating _TIF_NOHZ to decide whether to use the slow syscall entry path is not only pointless, it's actually counterproductive: 1) Context tracking code is invoked unconditionally before that flag is evaluated. 2) If the flag is set the slow path is invoked for nothing due to #1 Remove it. Signed-off-by: Thomas Gleixner Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Andy Lutomirski Signed-off-by: Frederic Weisbecker --- arch/x86/include/asm/thread_info.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index cf4327986e98..6cb9d1b0d1e6 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -133,14 +133,10 @@ struct thread_info { #define _TIF_X32 (1 << TIF_X32) #define _TIF_FSCHECK (1 << TIF_FSCHECK) -/* - * work to do in syscall_trace_enter(). Also includes TIF_NOHZ for - * enter_from_user_mode() - */ +/* Work to do before invoking the actual syscall. */ #define _TIF_WORK_SYSCALL_ENTRY \ (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_EMU | _TIF_SYSCALL_AUDIT | \ - _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT | \ - _TIF_NOHZ) + _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT) /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW_BASE \ From 490f561b783dac2c4825e288e6dbbf83481eea34 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 27 Jan 2020 16:41:52 +0100 Subject: [PATCH 0222/2231] context-tracking: Introduce CONFIG_HAVE_TIF_NOHZ A few archs (x86, arm, arm64) don't rely anymore on TIF_NOHZ to call into context tracking on user entry/exit but instead use static keys (or not) to optimize those calls. Ideally every arch should migrate to that behaviour in the long run. Settle a config option to let those archs remove their TIF_NOHZ definitions. Signed-off-by: Frederic Weisbecker Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Andy Lutomirski Cc: Russell King Cc: Catalin Marinas Cc: Will Deacon Cc: Ralf Baechle Cc: Paul Burton Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: David S. Miller --- arch/Kconfig | 16 +++++++++++----- arch/arm/Kconfig | 1 + arch/arm64/Kconfig | 1 + arch/mips/Kconfig | 1 + arch/powerpc/Kconfig | 1 + arch/sparc/Kconfig | 1 + arch/x86/Kconfig | 1 + kernel/context_tracking.c | 2 ++ 8 files changed, 19 insertions(+), 5 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index 98de654b79b3..dbf420a9f87b 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -540,11 +540,17 @@ config HAVE_CONTEXT_TRACKING help Provide kernel/user boundaries probes necessary for subsystems that need it, such as userspace RCU extended quiescent state. - Syscalls need to be wrapped inside user_exit()-user_enter() through - the slow path using TIF_NOHZ flag. Exceptions handlers must be - wrapped as well. Irqs are already protected inside - rcu_irq_enter/rcu_irq_exit() but preemption or signal handling on - irq exit still need to be protected. + Syscalls need to be wrapped inside user_exit()-user_enter(), either + optimized behind static key or through the slow path using TIF_NOHZ + flag. Exceptions handlers must be wrapped as well. Irqs are already + protected inside rcu_irq_enter/rcu_irq_exit() but preemption or signal + handling on irq exit still need to be protected. + +config HAVE_TIF_NOHZ + bool + help + Arch relies on TIF_NOHZ and syscall slow path to implement context + tracking calls to user_enter()/user_exit(). config HAVE_VIRT_CPU_ACCOUNTING bool diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 97864aabc2a6..38b764cae559 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -72,6 +72,7 @@ config ARM select HAVE_ARM_SMCCC if CPU_V7 select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 select HAVE_CONTEXT_TRACKING + select HAVE_TIF_NOHZ select HAVE_COPY_THREAD_TLS select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_KMEMLEAK if !XIP_KERNEL diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 0b30e884e088..5c945fa3df26 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -140,6 +140,7 @@ config ARM64 select HAVE_CMPXCHG_DOUBLE select HAVE_CMPXCHG_LOCAL select HAVE_CONTEXT_TRACKING + select HAVE_TIF_NOHZ select HAVE_COPY_THREAD_TLS select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 797d7f1ad5fe..2589d4760e45 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -51,6 +51,7 @@ config MIPS select HAVE_ASM_MODVERSIONS select HAVE_CBPF_JIT if !64BIT && !CPU_MICROMIPS select HAVE_CONTEXT_TRACKING + select HAVE_TIF_NOHZ select HAVE_COPY_THREAD_TLS select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_KMEMLEAK diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 497b7d0b2d7e..6f40af294685 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -182,6 +182,7 @@ config PPC select HAVE_STACKPROTECTOR if PPC64 && $(cc-option,-mstack-protector-guard=tls -mstack-protector-guard-reg=r13) select HAVE_STACKPROTECTOR if PPC32 && $(cc-option,-mstack-protector-guard=tls -mstack-protector-guard-reg=r2) select HAVE_CONTEXT_TRACKING if PPC64 + select HAVE_TIF_NOHZ if PPC64 select HAVE_COPY_THREAD_TLS select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_STACKOVERFLOW diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index c1dd6dd642f4..9cc9ab04bd99 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -71,6 +71,7 @@ config SPARC64 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_SYSCALL_TRACEPOINTS select HAVE_CONTEXT_TRACKING + select HAVE_TIF_NOHZ select HAVE_DEBUG_KMEMLEAK select IOMMU_HELPER select SPARSE_IRQ diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index beea77046f9b..549eed3460c9 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -211,6 +211,7 @@ config X86 select HAVE_STACK_VALIDATION if X86_64 select HAVE_RSEQ select HAVE_SYSCALL_TRACEPOINTS + select HAVE_TIF_NOHZ if X86_64 select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_USER_RETURN_NOTIFIER select HAVE_GENERIC_VDSO diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 0296b4bda8f1..ce430885c26c 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -198,11 +198,13 @@ void __init context_tracking_cpu_set(int cpu) if (initialized) return; +#ifdef CONFIG_HAVE_TIF_NOHZ /* * Set TIF_NOHZ to init/0 and let it propagate to all tasks through fork * This assumes that init is the only task at this early boot stage. */ set_tsk_thread_flag(&init_task, TIF_NOHZ); +#endif WARN_ON_ONCE(!tasklist_empty()); initialized = true; From 68d875131e43a8eed193810c1dfebb027fe2450e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 28 Jan 2020 13:50:32 +0100 Subject: [PATCH 0223/2231] x86: Remove TIF_NOHZ Static keys have replaced TIF_NOHZ to optimize the calls to context tracking. We can now safely remove that thread flag. Signed-off-by: Frederic Weisbecker Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Andy Lutomirski --- arch/x86/Kconfig | 1 - arch/x86/include/asm/thread_info.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 549eed3460c9..beea77046f9b 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -211,7 +211,6 @@ config X86 select HAVE_STACK_VALIDATION if X86_64 select HAVE_RSEQ select HAVE_SYSCALL_TRACEPOINTS - select HAVE_TIF_NOHZ if X86_64 select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_USER_RETURN_NOTIFIER select HAVE_GENERIC_VDSO diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 6cb9d1b0d1e6..384cdde10680 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -92,7 +92,6 @@ struct thread_info { #define TIF_NOCPUID 15 /* CPUID is not accessible in userland */ #define TIF_NOTSC 16 /* TSC is not accessible in userland */ #define TIF_IA32 17 /* IA32 compatibility process */ -#define TIF_NOHZ 19 /* in adaptive nohz mode */ #define TIF_MEMDIE 20 /* is terminating due to OOM killer */ #define TIF_POLLING_NRFLAG 21 /* idle is polling for TIF_NEED_RESCHED */ #define TIF_IO_BITMAP 22 /* uses I/O bitmap */ @@ -122,7 +121,6 @@ struct thread_info { #define _TIF_NOCPUID (1 << TIF_NOCPUID) #define _TIF_NOTSC (1 << TIF_NOTSC) #define _TIF_IA32 (1 << TIF_IA32) -#define _TIF_NOHZ (1 << TIF_NOHZ) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) #define _TIF_FORCED_TF (1 << TIF_FORCED_TF) From 1acb2249ee380f56ffb4200ac85bfe233d7a8ca3 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 28 Jan 2020 13:50:32 +0100 Subject: [PATCH 0224/2231] arm: Remove TIF_NOHZ Arm entry code calls context tracking from fast path. TIF_NOHZ is unused and can be safely removed. Signed-off-by: Frederic Weisbecker Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Russell King --- arch/arm/Kconfig | 1 - arch/arm/include/asm/thread_info.h | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 38b764cae559..97864aabc2a6 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -72,7 +72,6 @@ config ARM select HAVE_ARM_SMCCC if CPU_V7 select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 select HAVE_CONTEXT_TRACKING - select HAVE_TIF_NOHZ select HAVE_COPY_THREAD_TLS select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_KMEMLEAK if !XIP_KERNEL diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 0d0d5178e2c3..3609a6980c34 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -141,7 +141,6 @@ extern int vfp_restore_user_hwstate(struct user_vfp *, #define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ #define TIF_SECCOMP 7 /* seccomp syscall filtering active */ -#define TIF_NOHZ 12 /* in adaptive nohz mode */ #define TIF_USING_IWMMXT 17 #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ #define TIF_RESTORE_SIGMASK 20 From b7dec2cb19f3114c9e34431bb52ebe88db9b6363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Jan 2020 14:15:47 +0100 Subject: [PATCH 0225/2231] dt-bindings: iio: ltc2632: expand for ltc2636 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ltc2636 family of devices is register compatible with the ltc2636 chips, it just features 8 instead of 2 channels. Signed-off-by: Uwe Kleine-König Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/dac/ltc2632.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/dac/ltc2632.txt b/Documentation/devicetree/bindings/iio/dac/ltc2632.txt index e0d5fea33031..338c3220f01a 100644 --- a/Documentation/devicetree/bindings/iio/dac/ltc2632.txt +++ b/Documentation/devicetree/bindings/iio/dac/ltc2632.txt @@ -1,4 +1,4 @@ -Linear Technology LTC2632 DAC device driver +Linear Technology LTC2632/2636 DAC Required properties: - compatible: Has to contain one of the following: @@ -8,6 +8,12 @@ Required properties: lltc,ltc2632-h12 lltc,ltc2632-h10 lltc,ltc2632-h8 + lltc,ltc2636-l12 + lltc,ltc2636-l10 + lltc,ltc2636-l8 + lltc,ltc2636-h12 + lltc,ltc2636-h10 + lltc,ltc2636-h8 Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt apply. In particular, "reg" and "spi-max-frequency" properties must be given. From f243d0f0bd16c281ba5cce19eae86b433257fc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Jan 2020 14:15:48 +0100 Subject: [PATCH 0226/2231] iio: dac: ltc2632: drop some duplicated data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The channel data for ltc2632l12 and ltc2632h12 are identical. So there is no gain in using two different (but identical) channel descriptions. The only side effect of this change is some memory savings. Signed-off-by: Uwe Kleine-König Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ltc2632.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 643d1ce956ce..7ab92e178496 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -192,37 +192,33 @@ static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { LTC2632_CHANNEL(1, _bits), \ } -static DECLARE_LTC2632_CHANNELS(ltc2632l12, 12); -static DECLARE_LTC2632_CHANNELS(ltc2632l10, 10); -static DECLARE_LTC2632_CHANNELS(ltc2632l8, 8); - -static DECLARE_LTC2632_CHANNELS(ltc2632h12, 12); -static DECLARE_LTC2632_CHANNELS(ltc2632h10, 10); -static DECLARE_LTC2632_CHANNELS(ltc2632h8, 8); +static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12); +static DECLARE_LTC2632_CHANNELS(ltc2632x10, 10); +static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8); static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { [ID_LTC2632L12] = { - .channels = ltc2632l12_channels, + .channels = ltc2632x12_channels, .vref_mv = 2500, }, [ID_LTC2632L10] = { - .channels = ltc2632l10_channels, + .channels = ltc2632x10_channels, .vref_mv = 2500, }, [ID_LTC2632L8] = { - .channels = ltc2632l8_channels, + .channels = ltc2632x8_channels, .vref_mv = 2500, }, [ID_LTC2632H12] = { - .channels = ltc2632h12_channels, + .channels = ltc2632x12_channels, .vref_mv = 4096, }, [ID_LTC2632H10] = { - .channels = ltc2632h10_channels, + .channels = ltc2632x10_channels, .vref_mv = 4096, }, [ID_LTC2632H8] = { - .channels = ltc2632h8_channels, + .channels = ltc2632x8_channels, .vref_mv = 4096, }, }; From 9f15a4a0adc910a7bd535b755185f89c3e25b37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Jan 2020 14:15:49 +0100 Subject: [PATCH 0227/2231] iio: dac: ltc2632: add support for LTC2636 family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only difference between LTC2632 and LTC2636 is that the former has two DAC channels while the latter has eight. Signed-off-by: Uwe Kleine-König Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 5 ++- drivers/iio/dac/ltc2632.c | 77 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 979070196da9..3adbd2de38a1 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -132,11 +132,12 @@ config LTC1660 module will be called ltc1660. config LTC2632 - tristate "Linear Technology LTC2632-12/10/8 DAC spi driver" + tristate "Linear Technology LTC2632-12/10/8 and LTC2636-12/10/8 DAC spi driver" depends on SPI help Say yes here to build support for Linear Technology - LTC2632-12, LTC2632-10, LTC2632-8 converters (DAC). + LTC2632-12, LTC2632-10, LTC2632-8, LTC2636-12, LTC2636-10 and + LTC2636-8 converters (DAC). To compile this driver as a module, choose M here: the module will be called ltc2632. diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 7ab92e178496..1e7ffa0f6071 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -12,8 +12,6 @@ #include #include -#define LTC2632_DAC_CHANNELS 2 - #define LTC2632_ADDR_DAC0 0x0 #define LTC2632_ADDR_DAC1 0x1 @@ -33,6 +31,7 @@ */ struct ltc2632_chip_info { const struct iio_chan_spec *channels; + const size_t num_channels; const int vref_mv; }; @@ -57,6 +56,12 @@ enum ltc2632_supported_device_ids { ID_LTC2632H12, ID_LTC2632H10, ID_LTC2632H8, + ID_LTC2636L12, + ID_LTC2636L10, + ID_LTC2636L8, + ID_LTC2636H12, + ID_LTC2636H10, + ID_LTC2636H8, }; static int ltc2632_spi_write(struct spi_device *spi, @@ -190,6 +195,12 @@ static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { const struct iio_chan_spec _name ## _channels[] = { \ LTC2632_CHANNEL(0, _bits), \ LTC2632_CHANNEL(1, _bits), \ + LTC2632_CHANNEL(2, _bits), \ + LTC2632_CHANNEL(3, _bits), \ + LTC2632_CHANNEL(4, _bits), \ + LTC2632_CHANNEL(5, _bits), \ + LTC2632_CHANNEL(6, _bits), \ + LTC2632_CHANNEL(7, _bits), \ } static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12); @@ -199,26 +210,62 @@ static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8); static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = { [ID_LTC2632L12] = { .channels = ltc2632x12_channels, + .num_channels = 2, .vref_mv = 2500, }, [ID_LTC2632L10] = { .channels = ltc2632x10_channels, + .num_channels = 2, .vref_mv = 2500, }, [ID_LTC2632L8] = { .channels = ltc2632x8_channels, + .num_channels = 2, .vref_mv = 2500, }, [ID_LTC2632H12] = { .channels = ltc2632x12_channels, + .num_channels = 2, .vref_mv = 4096, }, [ID_LTC2632H10] = { .channels = ltc2632x10_channels, + .num_channels = 2, .vref_mv = 4096, }, [ID_LTC2632H8] = { .channels = ltc2632x8_channels, + .num_channels = 2, + .vref_mv = 4096, + }, + [ID_LTC2636L12] = { + .channels = ltc2632x12_channels, + .num_channels = 8, + .vref_mv = 2500, + }, + [ID_LTC2636L10] = { + .channels = ltc2632x10_channels, + .num_channels = 8, + .vref_mv = 2500, + }, + [ID_LTC2636L8] = { + .channels = ltc2632x8_channels, + .num_channels = 8, + .vref_mv = 2500, + }, + [ID_LTC2636H12] = { + .channels = ltc2632x12_channels, + .num_channels = 8, + .vref_mv = 4096, + }, + [ID_LTC2636H10] = { + .channels = ltc2632x10_channels, + .num_channels = 8, + .vref_mv = 4096, + }, + [ID_LTC2636H8] = { + .channels = ltc2632x8_channels, + .num_channels = 8, .vref_mv = 4096, }, }; @@ -287,7 +334,7 @@ static int ltc2632_probe(struct spi_device *spi) indio_dev->info = <c2632_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = chip_info->channels; - indio_dev->num_channels = LTC2632_DAC_CHANNELS; + indio_dev->num_channels = chip_info->num_channels; return iio_device_register(indio_dev); } @@ -312,6 +359,12 @@ static const struct spi_device_id ltc2632_id[] = { { "ltc2632-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] }, { "ltc2632-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] }, { "ltc2632-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] }, + { "ltc2636-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L12] }, + { "ltc2636-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L10] }, + { "ltc2636-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L8] }, + { "ltc2636-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H12] }, + { "ltc2636-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H10] }, + { "ltc2636-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H8] }, {} }; MODULE_DEVICE_TABLE(spi, ltc2632_id); @@ -335,6 +388,24 @@ static const struct of_device_id ltc2632_of_match[] = { }, { .compatible = "lltc,ltc2632-h8", .data = <c2632_chip_info_tbl[ID_LTC2632H8] + }, { + .compatible = "lltc,ltc2636-l12", + .data = <c2632_chip_info_tbl[ID_LTC2636L12] + }, { + .compatible = "lltc,ltc2636-l10", + .data = <c2632_chip_info_tbl[ID_LTC2636L10] + }, { + .compatible = "lltc,ltc2636-l8", + .data = <c2632_chip_info_tbl[ID_LTC2636L8] + }, { + .compatible = "lltc,ltc2636-h12", + .data = <c2632_chip_info_tbl[ID_LTC2636H12] + }, { + .compatible = "lltc,ltc2636-h10", + .data = <c2632_chip_info_tbl[ID_LTC2636H10] + }, { + .compatible = "lltc,ltc2636-h8", + .data = <c2632_chip_info_tbl[ID_LTC2636H8] }, {} }; From 55f82c00b2f345eed031ed189b16b1b2d8689901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 3 Feb 2020 21:31:26 +0100 Subject: [PATCH 0228/2231] iio: dac: ltc2632: remove some unused defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two defines are unused since the driver was introduced in commit 02b829f9e11f ("iio: dac: Add support for ltc2632 DACs"). Signed-off-by: Uwe Kleine-König Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ltc2632.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 1e7ffa0f6071..7adc91056aa1 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -12,9 +12,6 @@ #include #include -#define LTC2632_ADDR_DAC0 0x0 -#define LTC2632_ADDR_DAC1 0x1 - #define LTC2632_CMD_WRITE_INPUT_N 0x0 #define LTC2632_CMD_UPDATE_DAC_N 0x1 #define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 From 4a818643e9886c66ba65439a1cd9001c800d6fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 3 Feb 2020 10:17:01 +0100 Subject: [PATCH 0229/2231] iio: vcnl4000: Use a single return when getting IIO_CHAN_INFO_RAW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be useful when introducing runtime pm. Signed-off-by: Guido Günther Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index b0e241aaefb4..8f198383626b 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -335,17 +335,18 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_LIGHT: ret = data->chip_spec->measure_light(data, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; + if (!ret) + ret = IIO_VAL_INT; + break; case IIO_PROXIMITY: ret = data->chip_spec->measure_proximity(data, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; + if (!ret) + ret = IIO_VAL_INT; + break; default: - return -EINVAL; + ret = -EINVAL; } + return ret; case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_LIGHT) return -EINVAL; From 5e00708df3816d47d0de8b3837a33d64ff3582ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 3 Feb 2020 10:17:02 +0100 Subject: [PATCH 0230/2231] iio: vcnl4000: Enable runtime pm for vcnl4200/4040 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is modelled after the vcnl4035 driver. For the vcnl40{0,1,2}0 we don't do anything since they use on demand measurement. Signed-off-by: Guido Günther Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 129 +++++++++++++++++++++++++++++++---- 1 file changed, 117 insertions(+), 12 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 8f198383626b..38fcd9a26046 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,8 @@ #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ +#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ + enum vcnl4000_device_ids { VCNL4000, VCNL4010, @@ -87,6 +90,7 @@ struct vcnl4000_chip_spec { int (*init)(struct vcnl4000_data *data); int (*measure_light)(struct vcnl4000_data *data, int *val); int (*measure_proximity)(struct vcnl4000_data *data, int *val); + int (*set_power_state)(struct vcnl4000_data *data, bool on); }; static const struct i2c_device_id vcnl4000_id[] = { @@ -99,6 +103,12 @@ static const struct i2c_device_id vcnl4000_id[] = { }; MODULE_DEVICE_TABLE(i2c, vcnl4000_id); +static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) +{ + /* no suspend op */ + return 0; +} + static int vcnl4000_init(struct vcnl4000_data *data) { int ret, prod_id; @@ -127,9 +137,31 @@ static int vcnl4000_init(struct vcnl4000_data *data) data->al_scale = 250000; mutex_init(&data->vcnl4000_lock); - return 0; + return data->chip_spec->set_power_state(data, true); }; +static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) +{ + u16 val = on ? 0 /* power on */ : 1 /* shut down */; + int ret; + + ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); + if (ret < 0) + return ret; + + if (on) { + /* Wait at least one integration cycle before fetching data */ + data->vcnl4200_al.last_measurement = ktime_get(); + data->vcnl4200_ps.last_measurement = ktime_get(); + } + + return 0; +} + static int vcnl4200_init(struct vcnl4000_data *data) { int ret, id; @@ -155,14 +187,6 @@ static int vcnl4200_init(struct vcnl4000_data *data) data->rev = (ret >> 8) & 0xf; - /* Set defaults and enable both channels */ - ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 0); - if (ret < 0) - return ret; - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 0); - if (ret < 0) - return ret; - data->vcnl4200_al.reg = VCNL4200_AL_DATA; data->vcnl4200_ps.reg = VCNL4200_PS_DATA; switch (id) { @@ -180,11 +204,13 @@ static int vcnl4200_init(struct vcnl4000_data *data) data->al_scale = 120000; break; } - data->vcnl4200_al.last_measurement = ktime_set(0, 0); - data->vcnl4200_ps.last_measurement = ktime_set(0, 0); mutex_init(&data->vcnl4200_al.lock); mutex_init(&data->vcnl4200_ps.lock); + ret = data->chip_spec->set_power_state(data, true); + if (ret < 0) + return ret; + return 0; }; @@ -291,24 +317,28 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .init = vcnl4000_init, .measure_light = vcnl4000_measure_light, .measure_proximity = vcnl4000_measure_proximity, + .set_power_state = vcnl4000_set_power_state, }, [VCNL4010] = { .prod = "VCNL4010/4020", .init = vcnl4000_init, .measure_light = vcnl4000_measure_light, .measure_proximity = vcnl4000_measure_proximity, + .set_power_state = vcnl4000_set_power_state, }, [VCNL4040] = { .prod = "VCNL4040", .init = vcnl4200_init, .measure_light = vcnl4200_measure_light, .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, }, [VCNL4200] = { .prod = "VCNL4200", .init = vcnl4200_init, .measure_light = vcnl4200_measure_light, .measure_proximity = vcnl4200_measure_proximity, + .set_power_state = vcnl4200_set_power_state, }, }; @@ -323,6 +353,23 @@ static const struct iio_chan_spec vcnl4000_channels[] = { } }; +static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) +{ + struct device *dev = &data->client->dev; + int ret; + + if (on) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + } else { + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + } + + return ret; +} + static int vcnl4000_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -332,6 +379,10 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + ret = vcnl4000_set_pm_runtime_state(data, true); + if (ret < 0) + return ret; + switch (chan->type) { case IIO_LIGHT: ret = data->chip_spec->measure_light(data, val); @@ -346,6 +397,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, default: ret = -EINVAL; } + vcnl4000_set_pm_runtime_state(data, false); return ret; case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_LIGHT) @@ -394,7 +446,22 @@ static int vcnl4000_probe(struct i2c_client *client, indio_dev->name = VCNL4000_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - return devm_iio_device_register(&client->dev, indio_dev); + ret = pm_runtime_set_active(&client->dev); + if (ret < 0) + goto fail_poweroff; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto fail_poweroff; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + return 0; +fail_poweroff: + data->chip_spec->set_power_state(data, false); + return ret; } static const struct of_device_id vcnl_4000_of_match[] = { @@ -422,13 +489,51 @@ static const struct of_device_id vcnl_4000_of_match[] = { }; MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); +static int vcnl4000_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct vcnl4000_data *data = iio_priv(indio_dev); + + pm_runtime_dont_use_autosuspend(&client->dev); + pm_runtime_disable(&client->dev); + iio_device_unregister(indio_dev); + pm_runtime_set_suspended(&client->dev); + + return data->chip_spec->set_power_state(data, false); +} + +static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct vcnl4000_data *data = iio_priv(indio_dev); + + return data->chip_spec->set_power_state(data, false); +} + +static int __maybe_unused vcnl4000_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct vcnl4000_data *data = iio_priv(indio_dev); + + return data->chip_spec->set_power_state(data, true); +} + +static const struct dev_pm_ops vcnl4000_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend, + vcnl4000_runtime_resume, NULL) +}; + static struct i2c_driver vcnl4000_driver = { .driver = { .name = VCNL4000_DRV_NAME, + .pm = &vcnl4000_pm_ops, .of_match_table = vcnl_4000_of_match, }, .probe = vcnl4000_probe, .id_table = vcnl4000_id, + .remove = vcnl4000_remove, }; module_i2c_driver(vcnl4000_driver); From 816abe42b8bde912c58d862d540a2f57ba967477 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 3 Feb 2020 17:09:16 +0200 Subject: [PATCH 0231/2231] dt-binding: iio: add NPCM ADC reset support Add NPCM ADC reset binding documentation. Signed-off-by: Tomer Maimon Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt index eb939fe77836..ef8eeec1a997 100644 --- a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt @@ -6,6 +6,7 @@ Required properties: - compatible: "nuvoton,npcm750-adc" for the NPCM7XX BMC. - reg: specifies physical base address and size of the registers. - interrupts: Contain the ADC interrupt with flags for falling edge. +- resets : phandle to the reset control for this device. Optional properties: - clocks: phandle of ADC reference clock, in case the clock is not @@ -21,4 +22,5 @@ adc: adc@f000c000 { reg = <0xf000c000 0x8>; interrupts = ; clocks = <&clk NPCM7XX_CLK_ADC>; + resets = <&rstc NPCM7XX_RESET_IPSRST1 NPCM7XX_RESET_ADC>; }; From db6bcb8c25884a29f57660857c788afca20a32e1 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 3 Feb 2020 17:09:17 +0200 Subject: [PATCH 0232/2231] iio: adc: modify NPCM reset support Modify NPCM ADC reset support from direct register access to reset controller support. please make sure to modify NPCM adc device tree parameters as described at nuvoton,npcm-adc.txt document for using this change. Note for anyone noting that this is a breaking change, this is on a BMC and effectively in a close ecosystem so it is fine to rely on DT and kernel being updated together. Signed-off-by: Tomer Maimon Signed-off-by: Jonathan Cameron --- drivers/iio/adc/npcm_adc.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index a6170a37ebe8..83bad2d5575d 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -14,6 +14,7 @@ #include #include #include +#include struct npcm_adc { bool int_status; @@ -23,13 +24,9 @@ struct npcm_adc { struct clk *adc_clk; wait_queue_head_t wq; struct regulator *vref; - struct regmap *rst_regmap; + struct reset_control *reset; }; -/* NPCM7xx reset module */ -#define NPCM7XX_IPSRST1_OFFSET 0x020 -#define NPCM7XX_IPSRST1_ADC_RST BIT(27) - /* ADC registers */ #define NPCM_ADCCON 0x00 #define NPCM_ADCDATA 0x04 @@ -106,13 +103,11 @@ static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel) msecs_to_jiffies(10)); if (ret == 0) { regtemp = ioread32(info->regs + NPCM_ADCCON); - if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) { + if (regtemp & NPCM_ADCCON_ADC_CONV) { /* if conversion failed - reset ADC module */ - regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET, - NPCM7XX_IPSRST1_ADC_RST); + reset_control_assert(info->reset); msleep(100); - regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET, - 0x0); + reset_control_deassert(info->reset); msleep(100); /* Enable ADC and start conversion module */ @@ -186,7 +181,6 @@ static int npcm_adc_probe(struct platform_device *pdev) struct npcm_adc *info; struct iio_dev *indio_dev; struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); if (!indio_dev) @@ -199,6 +193,10 @@ static int npcm_adc_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); + info->reset = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(info->reset)) + return PTR_ERR(info->reset); + info->adc_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(info->adc_clk)) { dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n"); @@ -211,16 +209,6 @@ static int npcm_adc_probe(struct platform_device *pdev) div = div >> NPCM_ADCCON_DIV_SHIFT; info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); - if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) { - info->rst_regmap = syscon_regmap_lookup_by_compatible - ("nuvoton,npcm750-rst"); - if (IS_ERR(info->rst_regmap)) { - dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n"); - ret = PTR_ERR(info->rst_regmap); - goto err_disable_clk; - } - } - irq = platform_get_irq(pdev, 0); if (irq <= 0) { ret = -EINVAL; From 4235cc30c906a4ddc59e9aa3db2e06d3faffcb3a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:00 +0100 Subject: [PATCH 0233/2231] iio: imu: inv_mpu6050: cleanup of/acpi support Use device_get_match_data for using of and acpi tables. In spi support add missing mpu6515 and of match table. Reorganize Kconfig to display chips grouped by generations. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 8 ++--- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 26 +++----------- drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 44 +++++++++++++++++++++-- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 017bc0fcc365..003134734646 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -15,8 +15,8 @@ config INV_MPU6050_I2C select INV_MPU6050_IIO select REGMAP_I2C help - This driver supports the Invensense MPU6050/6500/6515, - MPU9150/9250/9255 and ICM20608/20602 motion tracking devices + This driver supports the Invensense MPU6050/9150, + MPU6500/6515/9250/9255, ICM20608 and ICM20602 motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -27,8 +27,8 @@ config INV_MPU6050_SPI select INV_MPU6050_IIO select REGMAP_SPI help - This driver supports the Invensense MPU6000/6500/6515, - MPU9250/9255 and ICM20608/20602 motion tracking devices + This driver supports the Invensense MPU6000, + MPU6500/6515/9250/9255, ICM20608 and ICM20602 motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index f47a28b4be23..49e9aa777ba4 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "inv_mpu_iio.h" static const struct regmap_config inv_mpu_regmap_config = { @@ -54,20 +55,6 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) return 0; } -static const char *inv_mpu_match_acpi_device(struct device *dev, - enum inv_devices *chip_id) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - *chip_id = (int)id->driver_data; - - return dev_name(dev); -} - static bool inv_mpu_i2c_aux_bus(struct device *dev) { struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); @@ -130,6 +117,7 @@ static int inv_mpu_magn_disable(struct iio_dev *indio_dev) static int inv_mpu_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const void *match; struct inv_mpu6050_state *st; int result; enum inv_devices chip_type; @@ -140,18 +128,14 @@ static int inv_mpu_probe(struct i2c_client *client, I2C_FUNC_SMBUS_I2C_BLOCK)) return -EOPNOTSUPP; - if (client->dev.of_node) { - chip_type = (enum inv_devices) - of_device_get_match_data(&client->dev); + match = device_get_match_data(&client->dev); + if (match) { + chip_type = (enum inv_devices)match; name = client->name; } else if (id) { chip_type = (enum inv_devices) id->driver_data; name = id->name; - } else if (ACPI_HANDLE(&client->dev)) { - name = inv_mpu_match_acpi_device(&client->dev, &chip_type); - if (!name) - return -ENODEV; } else { return -ENOSYS; } diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index ec102d5a5c77..834bfa8e8684 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -4,6 +4,8 @@ */ #include #include +#include +#include #include #include #include @@ -41,17 +43,18 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev) static int inv_mpu_probe(struct spi_device *spi) { + const void *match; struct regmap *regmap; const struct spi_device_id *spi_id; - const struct acpi_device_id *acpi_id; const char *name = NULL; enum inv_devices chip_type; if ((spi_id = spi_get_device_id(spi))) { chip_type = (enum inv_devices)spi_id->driver_data; name = spi_id->name; - } else if ((acpi_id = acpi_match_device(spi->dev.driver->acpi_match_table, &spi->dev))) { - chip_type = (enum inv_devices)acpi_id->driver_data; + } else if ((match = device_get_match_data(&spi->dev))) { + chip_type = (enum inv_devices)match; + name = dev_name(&spi->dev); } else { return -ENODEV; } @@ -74,6 +77,7 @@ static int inv_mpu_probe(struct spi_device *spi) static const struct spi_device_id inv_mpu_id[] = { {"mpu6000", INV_MPU6000}, {"mpu6500", INV_MPU6500}, + {"mpu6515", INV_MPU6515}, {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, @@ -83,6 +87,39 @@ static const struct spi_device_id inv_mpu_id[] = { MODULE_DEVICE_TABLE(spi, inv_mpu_id); +static const struct of_device_id inv_of_match[] = { + { + .compatible = "invensense,mpu6000", + .data = (void *)INV_MPU6000 + }, + { + .compatible = "invensense,mpu6500", + .data = (void *)INV_MPU6500 + }, + { + .compatible = "invensense,mpu6515", + .data = (void *)INV_MPU6515 + }, + { + .compatible = "invensense,mpu9250", + .data = (void *)INV_MPU9250 + }, + { + .compatible = "invensense,mpu9255", + .data = (void *)INV_MPU9255 + }, + { + .compatible = "invensense,icm20608", + .data = (void *)INV_ICM20608 + }, + { + .compatible = "invensense,icm20602", + .data = (void *)INV_ICM20602 + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_of_match); + static const struct acpi_device_id inv_acpi_match[] = { {"INVN6000", INV_MPU6000}, { }, @@ -93,6 +130,7 @@ static struct spi_driver inv_mpu_driver = { .probe = inv_mpu_probe, .id_table = inv_mpu_id, .driver = { + .of_match_table = inv_of_match, .acpi_match_table = ACPI_PTR(inv_acpi_match), .name = "inv-mpu6000-spi", .pm = &inv_mpu_pmops, From 23db8d6959314568b243fd2bc76bee50136afa4c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:01 +0100 Subject: [PATCH 0234/2231] iio: imu: inv_mpu6050: add support of ICM20609 & ICM20689 They are similar to ICM20608 chip. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 8 +++---- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 26 ++++++++++++++++++---- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 12 ++++++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 5 +++++ drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 10 +++++++++ 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 003134734646..e933db3a5a89 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -16,8 +16,8 @@ config INV_MPU6050_I2C select REGMAP_I2C help This driver supports the Invensense MPU6050/9150, - MPU6500/6515/9250/9255, ICM20608 and ICM20602 motion tracking devices - over I2C. + MPU6500/6515/9250/9255, ICM20608/20609/20689 and ICM20602 + motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -28,7 +28,7 @@ config INV_MPU6050_SPI select REGMAP_SPI help This driver supports the Invensense MPU6000, - MPU6500/6515/9250/9255, ICM20608 and ICM20602 motion tracking devices - over SPI. + MPU6500/6515/9250/9255, ICM20608/20609/20689 and ICM20602 + motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 5096fc49012d..5fd57a1bee90 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -176,6 +176,22 @@ static const struct inv_mpu6050_hw hw_info[] = { .fifo_size = 512, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, + { + .whoami = INV_ICM20609_WHOAMI_VALUE, + .name = "ICM20609", + .reg = ®_set_6500, + .config = &chip_config_6050, + .fifo_size = 4 * 1024, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + }, + { + .whoami = INV_ICM20689_WHOAMI_VALUE, + .name = "ICM20689", + .reg = ®_set_6500, + .config = &chip_config_6050, + .fifo_size = 4 * 1024, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + }, { .whoami = INV_ICM20602_WHOAMI_VALUE, .name = "ICM20602", @@ -286,20 +302,22 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, if (result) return result; + /* set accel lpf */ switch (st->chip_type) { case INV_MPU6050: case INV_MPU6000: case INV_MPU9150: /* old chips, nothing to do */ - result = 0; + return 0; + case INV_ICM20689: + /* set FIFO size to maximum value */ + val |= INV_ICM20689_BITS_FIFO_SIZE_MAX; break; default: - /* set accel lpf */ - result = regmap_write(st->map, st->reg->accel_lpf, val); break; } - return result; + return regmap_write(st->map, st->reg->accel_lpf, val); } /** diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 49e9aa777ba4..dd054e21216a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -61,6 +61,8 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev) switch (st->chip_type) { case INV_ICM20608: + case INV_ICM20609: + case INV_ICM20689: case INV_ICM20602: /* no i2c auxiliary bus on the chip */ return false; @@ -202,6 +204,8 @@ static const struct i2c_device_id inv_mpu_id[] = { {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, + {"icm20609", INV_ICM20609}, + {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, {} }; @@ -237,6 +241,14 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20608", .data = (void *)INV_ICM20608 }, + { + .compatible = "invensense,icm20609", + .data = (void *)INV_ICM20609 + }, + { + .compatible = "invensense,icm20689", + .data = (void *)INV_ICM20689 + }, { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 6158fca7f70e..75b8903450eb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -75,6 +75,8 @@ enum inv_devices { INV_MPU9250, INV_MPU9255, INV_ICM20608, + INV_ICM20609, + INV_ICM20689, INV_ICM20602, INV_NUM_PARTS }; @@ -276,6 +278,7 @@ struct inv_mpu6050_state { /* mpu6500 registers */ #define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D +#define INV_ICM20689_BITS_FIFO_SIZE_MAX 0xC0 #define INV_MPU6500_REG_ACCEL_OFFSET 0x77 /* delay time in milliseconds */ @@ -340,6 +343,8 @@ struct inv_mpu6050_state { #define INV_MPU9255_WHOAMI_VALUE 0x73 #define INV_MPU6515_WHOAMI_VALUE 0x74 #define INV_ICM20608_WHOAMI_VALUE 0xAF +#define INV_ICM20609_WHOAMI_VALUE 0xA6 +#define INV_ICM20689_WHOAMI_VALUE 0x98 #define INV_ICM20602_WHOAMI_VALUE 0x12 /* scan element definition for generic MPU6xxx devices */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 834bfa8e8684..3803c40e7b24 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -81,6 +81,8 @@ static const struct spi_device_id inv_mpu_id[] = { {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, + {"icm20609", INV_ICM20609}, + {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, {} }; @@ -112,6 +114,14 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20608", .data = (void *)INV_ICM20608 }, + { + .compatible = "invensense,icm20609", + .data = (void *)INV_ICM20609 + }, + { + .compatible = "invensense,icm20689", + .data = (void *)INV_ICM20689 + }, { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 From d31f74e60804cba1d7b265708b820e524e574611 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:02 +0100 Subject: [PATCH 0235/2231] iio: imu: inv_mpu6050: add support of IAM20680 Automotive certified IMU, similar to ICM20608. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 4 ++-- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 8 ++++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 6 ++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 2 ++ drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 5 +++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index e933db3a5a89..e65474a64675 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -16,7 +16,7 @@ config INV_MPU6050_I2C select REGMAP_I2C help This driver supports the Invensense MPU6050/9150, - MPU6500/6515/9250/9255, ICM20608/20609/20689 and ICM20602 + MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602 and IAM20680 motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -28,7 +28,7 @@ config INV_MPU6050_SPI select REGMAP_SPI help This driver supports the Invensense MPU6000, - MPU6500/6515/9250/9255, ICM20608/20609/20689 and ICM20602 + MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602 and IAM20680 motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 5fd57a1bee90..e8e6beef3f04 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -200,6 +200,14 @@ static const struct inv_mpu6050_hw hw_info[] = { .fifo_size = 1008, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, + { + .whoami = INV_IAM20680_WHOAMI_VALUE, + .name = "IAM20680", + .reg = ®_set_6500, + .config = &chip_config_6050, + .fifo_size = 512, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + }, }; int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index dd054e21216a..cd0eeb5dc7f6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -64,6 +64,7 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev) case INV_ICM20609: case INV_ICM20689: case INV_ICM20602: + case INV_IAM20680: /* no i2c auxiliary bus on the chip */ return false; case INV_MPU9150: @@ -207,6 +208,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"icm20609", INV_ICM20609}, {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, + {"iam20680", INV_IAM20680}, {} }; @@ -253,6 +255,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 }, + { + .compatible = "invensense,iam20680", + .data = (void *)INV_IAM20680 + }, { } }; MODULE_DEVICE_TABLE(of, inv_of_match); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 75b8903450eb..404c289f8c84 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -78,6 +78,7 @@ enum inv_devices { INV_ICM20609, INV_ICM20689, INV_ICM20602, + INV_IAM20680, INV_NUM_PARTS }; @@ -346,6 +347,7 @@ struct inv_mpu6050_state { #define INV_ICM20609_WHOAMI_VALUE 0xA6 #define INV_ICM20689_WHOAMI_VALUE 0x98 #define INV_ICM20602_WHOAMI_VALUE 0x12 +#define INV_IAM20680_WHOAMI_VALUE 0xA9 /* scan element definition for generic MPU6xxx devices */ enum inv_mpu6050_scan { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 3803c40e7b24..393cec555be6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -84,6 +84,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"icm20609", INV_ICM20609}, {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, + {"iam20680", INV_IAM20680}, {} }; @@ -126,6 +127,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 }, + { + .compatible = "invensense,iam20680", + .data = (void *)INV_IAM20680 + }, { } }; MODULE_DEVICE_TABLE(of, inv_of_match); From 8abce87d39912e57ade3c141049ffa2eea85846b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:03 +0100 Subject: [PATCH 0236/2231] iio: imu: inv_mpu6050: add support of ICM20690 Same generation as ICM20602 but different registers. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/Kconfig | 8 ++--- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 36 +++++++++++++++++++--- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 5 +++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 3 ++ drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 5 +++ 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index e65474a64675..7137ea6f25db 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -16,8 +16,8 @@ config INV_MPU6050_I2C select REGMAP_I2C help This driver supports the Invensense MPU6050/9150, - MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602 and IAM20680 - motion tracking devices over I2C. + MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and + IAM20680 motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -28,7 +28,7 @@ config INV_MPU6050_SPI select REGMAP_SPI help This driver supports the Invensense MPU6000, - MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602 and IAM20680 - motion tracking devices over SPI. + MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and + IAM20680 motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index e8e6beef3f04..9ecc667debbe 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -200,6 +200,14 @@ static const struct inv_mpu6050_hw hw_info[] = { .fifo_size = 1008, .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, + { + .whoami = INV_ICM20690_WHOAMI_VALUE, + .name = "ICM20690", + .reg = ®_set_icm20602, + .config = &chip_config_6050, + .fifo_size = 1024, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + }, { .whoami = INV_IAM20680_WHOAMI_VALUE, .name = "IAM20680", @@ -295,6 +303,25 @@ int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) } EXPORT_SYMBOL_GPL(inv_mpu6050_set_power_itg); +static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st, + enum inv_mpu6050_fsr_e val) +{ + unsigned int gyro_shift; + u8 data; + + switch (st->chip_type) { + case INV_ICM20690: + gyro_shift = INV_ICM20690_GYRO_CONFIG_FSR_SHIFT; + break; + default: + gyro_shift = INV_MPU6050_GYRO_CONFIG_FSR_SHIFT; + break; + } + + data = val << gyro_shift; + return regmap_write(st->map, st->reg->gyro_config, data); +} + /** * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent * @@ -318,6 +345,7 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, /* old chips, nothing to do */ return 0; case INV_ICM20689: + case INV_ICM20690: /* set FIFO size to maximum value */ val |= INV_ICM20689_BITS_FIFO_SIZE_MAX; break; @@ -346,8 +374,8 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) result = inv_mpu6050_set_power_itg(st, true); if (result) return result; - d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); - result = regmap_write(st->map, st->reg->gyro_config, d); + + result = inv_mpu6050_set_gyro_fsr(st, INV_MPU6050_FSR_2000DPS); if (result) goto error_power_off; @@ -559,12 +587,10 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val) { int result, i; - u8 d; for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) { if (gyro_scale_6050[i] == val) { - d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); - result = regmap_write(st->map, st->reg->gyro_config, d); + result = inv_mpu6050_set_gyro_fsr(st, i); if (result) return result; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index cd0eeb5dc7f6..1363d3776523 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -208,6 +208,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"icm20609", INV_ICM20609}, {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, + {"icm20690", INV_ICM20690}, {"iam20680", INV_IAM20680}, {} }; @@ -255,6 +256,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 }, + { + .compatible = "invensense,icm20690", + .data = (void *)INV_ICM20690 + }, { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 404c289f8c84..7ae614052210 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -78,6 +78,7 @@ enum inv_devices { INV_ICM20609, INV_ICM20689, INV_ICM20602, + INV_ICM20690, INV_IAM20680, INV_NUM_PARTS }; @@ -297,6 +298,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_MAX_ACCL_FS_PARAM 3 #define INV_MPU6050_THREE_AXIS 3 #define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT 3 +#define INV_ICM20690_GYRO_CONFIG_FSR_SHIFT 2 #define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT 3 #define INV_MPU6500_TEMP_OFFSET 7011 @@ -347,6 +349,7 @@ struct inv_mpu6050_state { #define INV_ICM20609_WHOAMI_VALUE 0xA6 #define INV_ICM20689_WHOAMI_VALUE 0x98 #define INV_ICM20602_WHOAMI_VALUE 0x12 +#define INV_ICM20690_WHOAMI_VALUE 0x20 #define INV_IAM20680_WHOAMI_VALUE 0xA9 /* scan element definition for generic MPU6xxx devices */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 393cec555be6..bc351dd58c53 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -84,6 +84,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"icm20609", INV_ICM20609}, {"icm20689", INV_ICM20689}, {"icm20602", INV_ICM20602}, + {"icm20690", INV_ICM20690}, {"iam20680", INV_IAM20680}, {} }; @@ -127,6 +128,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,icm20602", .data = (void *)INV_ICM20602 }, + { + .compatible = "invensense,icm20690", + .data = (void *)INV_ICM20690 + }, { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 From 9fc89f796513ca6ce2a3a8f56b90ede7c3d42ba5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:04 +0100 Subject: [PATCH 0237/2231] iio: imu: inv_mpu6050: update LPF bandwidth settings As every chip has some little variant in LPF bandwidth values, use common values that are working for all chips. Simplify the LPF setting function. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 30 ++++++++++++---------- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 10 ++++---- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 9ecc667debbe..c4db9086775c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -707,30 +707,32 @@ error_write_raw_unlock: /** * inv_mpu6050_set_lpf() - set low pass filer based on fifo rate. * - * Based on the Nyquist principle, the sampling rate must - * exceed twice of the bandwidth of the signal, or there - * would be alising. This function basically search for the - * correct low pass parameters based on the fifo rate, e.g, - * sampling frequency. + * Based on the Nyquist principle, the bandwidth of the low + * pass filter must not exceed the signal sampling rate divided + * by 2, or there would be aliasing. + * This function basically search for the correct low pass + * parameters based on the fifo rate, e.g, sampling frequency. * * lpf is set automatically when setting sampling rate to avoid any aliases. */ static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate) { - static const int hz[] = {188, 98, 42, 20, 10, 5}; + static const int hz[] = {400, 200, 90, 40, 20, 10}; static const int d[] = { - INV_MPU6050_FILTER_188HZ, INV_MPU6050_FILTER_98HZ, - INV_MPU6050_FILTER_42HZ, INV_MPU6050_FILTER_20HZ, + INV_MPU6050_FILTER_200HZ, INV_MPU6050_FILTER_100HZ, + INV_MPU6050_FILTER_45HZ, INV_MPU6050_FILTER_20HZ, INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ }; - int i, h, result; + int i, result; u8 data; - h = (rate >> 1); - i = 0; - while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1)) - i++; - data = d[i]; + data = INV_MPU6050_FILTER_5HZ; + for (i = 0; i < ARRAY_SIZE(hz); ++i) { + if (rate >= hz[i]) { + data = d[i]; + break; + } + } result = inv_mpu6050_set_lpf_regs(st, data); if (result) return result; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 7ae614052210..9a81098a8b4d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -370,14 +370,14 @@ enum inv_mpu6050_scan { }; enum inv_mpu6050_filter_e { - INV_MPU6050_FILTER_256HZ_NOLPF2 = 0, - INV_MPU6050_FILTER_188HZ, - INV_MPU6050_FILTER_98HZ, - INV_MPU6050_FILTER_42HZ, + INV_MPU6050_FILTER_NOLPF2 = 0, + INV_MPU6050_FILTER_200HZ, + INV_MPU6050_FILTER_100HZ, + INV_MPU6050_FILTER_45HZ, INV_MPU6050_FILTER_20HZ, INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ, - INV_MPU6050_FILTER_2100HZ_NOLPF, + INV_MPU6050_FILTER_NOLPF, NUM_MPU6050_FILTER }; From b9cd7be04b0e6cb844f4c64ce05ba892095eb521 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Maneyrol Date: Thu, 6 Feb 2020 11:31:05 +0100 Subject: [PATCH 0238/2231] dt-bindings: add description for new supported chips Add missing mpu6000 and new icm20609, icm20689, icm20690, and iam20680. Signed-off-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt index c5ee8a20af9f..f2f64749e818 100644 --- a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt +++ b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -4,6 +4,7 @@ http://www.invensense.com/mems/gyro/mpu6050.html Required properties: - compatible : should be one of + "invensense,mpu6000" "invensense,mpu6050" "invensense,mpu6500" "invensense,mpu6515" @@ -11,7 +12,11 @@ Required properties: "invensense,mpu9250" "invensense,mpu9255" "invensense,icm20608" + "invensense,icm20609" + "invensense,icm20689" "invensense,icm20602" + "invensense,icm20690" + "invensense,iam20680" - reg : the I2C address of the sensor - interrupts: interrupt mapping for IRQ. It should be configured with flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or From c77761d660a64d50f5620de43173d94f71d3d2d0 Mon Sep 17 00:00:00 2001 From: Lukasz Szczesny Date: Thu, 13 Feb 2020 00:08:34 +0100 Subject: [PATCH 0239/2231] staging: rtl8723bs: Fix spacing issues This patch fixes spacing issues reported by checkpatch. Signed-off-by: Lukasz Szczesny Link: https://lore.kernel.org/r/20200212230834.GA294323@localhost Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 +- drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h | 6 +- drivers/staging/rtl8723bs/hal/odm.h | 2 +- drivers/staging/rtl8723bs/hal/odm_HWConfig.c | 2 +- drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c | 4 +- .../staging/rtl8723bs/hal/rtl8723bs_xmit.c | 2 +- drivers/staging/rtl8723bs/include/HalVerDef.h | 32 +- drivers/staging/rtl8723bs/include/cmd_osdep.h | 4 +- drivers/staging/rtl8723bs/include/drv_types.h | 10 +- drivers/staging/rtl8723bs/include/hal_com.h | 98 +++--- .../staging/rtl8723bs/include/hal_com_h2c.h | 8 +- .../rtl8723bs/include/hal_com_phycfg.h | 2 +- .../staging/rtl8723bs/include/hal_com_reg.h | 14 +- drivers/staging/rtl8723bs/include/hal_intf.h | 2 +- drivers/staging/rtl8723bs/include/hal_phy.h | 2 +- .../staging/rtl8723bs/include/hal_phy_cfg.h | 4 +- .../staging/rtl8723bs/include/hal_pwr_seq.h | 4 +- drivers/staging/rtl8723bs/include/ieee80211.h | 22 +- .../staging/rtl8723bs/include/osdep_intf.h | 2 +- .../staging/rtl8723bs/include/osdep_service.h | 10 +- .../rtl8723bs/include/osdep_service_linux.h | 2 +- .../staging/rtl8723bs/include/recv_osdep.h | 4 +- .../staging/rtl8723bs/include/rtl8723b_cmd.h | 2 +- .../staging/rtl8723bs/include/rtl8723b_rf.h | 2 +- .../staging/rtl8723bs/include/rtl8723b_xmit.h | 2 +- .../staging/rtl8723bs/include/rtw_byteorder.h | 2 +- drivers/staging/rtl8723bs/include/rtw_cmd.h | 106 +++---- drivers/staging/rtl8723bs/include/rtw_debug.h | 28 +- .../staging/rtl8723bs/include/rtw_eeprom.h | 2 +- drivers/staging/rtl8723bs/include/rtw_efuse.h | 8 +- drivers/staging/rtl8723bs/include/rtw_event.h | 2 +- drivers/staging/rtl8723bs/include/rtw_ht.h | 12 +- drivers/staging/rtl8723bs/include/rtw_io.h | 6 +- drivers/staging/rtl8723bs/include/rtw_mlme.h | 66 ++--- .../staging/rtl8723bs/include/rtw_mlme_ext.h | 40 +-- drivers/staging/rtl8723bs/include/rtw_mp.h | 16 +- drivers/staging/rtl8723bs/include/rtw_recv.h | 18 +- .../staging/rtl8723bs/include/rtw_security.h | 30 +- drivers/staging/rtl8723bs/include/rtw_xmit.h | 26 +- drivers/staging/rtl8723bs/include/sta_info.h | 8 +- drivers/staging/rtl8723bs/include/wifi.h | 8 +- .../staging/rtl8723bs/include/xmit_osdep.h | 4 +- .../staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 124 ++++---- .../staging/rtl8723bs/os_dep/ioctl_linux.c | 280 +++++++++--------- drivers/staging/rtl8723bs/os_dep/mlme_linux.c | 6 +- drivers/staging/rtl8723bs/os_dep/os_intfs.c | 12 +- drivers/staging/rtl8723bs/os_dep/recv_linux.c | 8 +- drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 12 +- drivers/staging/rtl8723bs/os_dep/xmit_linux.c | 6 +- 49 files changed, 537 insertions(+), 537 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c index 13a9b54b4561..61a9bf61b976 100644 --- a/drivers/staging/rtl8723bs/core/rtw_cmd.c +++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c @@ -201,7 +201,7 @@ exit: return res; } -static void c2h_wk_callback(_workitem *work); +static void c2h_wk_callback(_workitem * work); int rtw_init_evt_priv(struct evt_priv *pevtpriv) { /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */ diff --git a/drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h b/drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h index 7150d54d49ab..c758d143c57f 100644 --- a/drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h +++ b/drivers/staging/rtl8723bs/hal/HalBtcOutSrc.h @@ -44,14 +44,14 @@ #define BTC_ANT_WIFI_AT_CPL_MAIN 0 #define BTC_ANT_WIFI_AT_CPL_AUX 1 -typedef enum _BTC_POWERSAVE_TYPE{ +typedef enum _BTC_POWERSAVE_TYPE { BTC_PS_WIFI_NATIVE = 0, /* wifi original power save behavior */ BTC_PS_LPS_ON = 1, BTC_PS_LPS_OFF = 2, BTC_PS_MAX } BTC_POWERSAVE_TYPE, *PBTC_POWERSAVE_TYPE; -typedef enum _BTC_BT_REG_TYPE{ +typedef enum _BTC_BT_REG_TYPE { BTC_BT_REG_RF = 0, BTC_BT_REG_MODEM = 1, BTC_BT_REG_BLUEWIZE = 2, @@ -60,7 +60,7 @@ typedef enum _BTC_BT_REG_TYPE{ BTC_BT_REG_MAX } BTC_BT_REG_TYPE, *PBTC_BT_REG_TYPE; -typedef enum _BTC_CHIP_INTERFACE{ +typedef enum _BTC_CHIP_INTERFACE { BTC_INTF_UNKNOWN = 0, BTC_INTF_PCI = 1, BTC_INTF_USB = 2, diff --git a/drivers/staging/rtl8723bs/hal/odm.h b/drivers/staging/rtl8723bs/hal/odm.h index fba3b9e1491b..b77d1fe33a28 100644 --- a/drivers/staging/rtl8723bs/hal/odm.h +++ b/drivers/staging/rtl8723bs/hal/odm.h @@ -849,7 +849,7 @@ typedef struct _ODM_PATH_DIVERSITY_ { u32 PathB_Cnt[ODM_ASSOCIATE_ENTRY_NUM]; } PATHDIV_T, *pPATHDIV_T; -typedef enum _BASEBAND_CONFIG_PHY_REG_PG_VALUE_TYPE{ +typedef enum _BASEBAND_CONFIG_PHY_REG_PG_VALUE_TYPE { PHY_REG_PG_RELATIVE_VALUE = 0, PHY_REG_PG_EXACT_VALUE = 1 } PHY_REG_PG_TYPE; diff --git a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c index 71919a3d81ab..9950934c3ab3 100644 --- a/drivers/staging/rtl8723bs/hal/odm_HWConfig.c +++ b/drivers/staging/rtl8723bs/hal/odm_HWConfig.c @@ -106,7 +106,7 @@ static void odm_RxPhyStatus92CSeries_Parsing( /* (2)PWDB, Average PWDB cacluated by hardware (for rate adaptive) */ /* */ - cck_agc_rpt = pPhyStaRpt->cck_agc_rpt_ofdm_cfosho_a ; + cck_agc_rpt = pPhyStaRpt->cck_agc_rpt_ofdm_cfosho_a; /* 2011.11.28 LukeLee: 88E use different LNA & VGA gain table */ /* The RSSI formula should be modified according to the gain table */ diff --git a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c index 71b5a50b6ef6..a69e8ae16565 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723b_cmd.c @@ -146,7 +146,7 @@ static void ConstructBeacon(struct adapter *padapter, u8 *pframe, u32 *pLength) SetFrameSubType(pframe, WIFI_BEACON); pframe += sizeof(struct ieee80211_hdr_3addr); - pktlen = sizeof (struct ieee80211_hdr_3addr); + pktlen = sizeof(struct ieee80211_hdr_3addr); /* timestamp will be inserted by hardware */ pframe += 8; @@ -1652,7 +1652,7 @@ static void rtl8723b_set_FwRsvdPagePkt( TotalPacketLen = BufIndex-TxDescLen + 256; /* extension memory for FW */ #else - TotalPacketLen = BufIndex-TxDescLen + sizeof (union pn48); /* IV len */ + TotalPacketLen = BufIndex - TxDescLen + sizeof(union pn48); /* IV len */ #endif /* CONFIG_GTK_OL */ } else #endif /* CONFIG_WOWLAN */ diff --git a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c index b44e902ed338..a5553e50218b 100644 --- a/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c +++ b/drivers/staging/rtl8723bs/hal/rtl8723bs_xmit.c @@ -282,7 +282,7 @@ static s32 xmit_xmitframes(struct adapter *padapter, struct xmit_priv *pxmitpriv /* check xmit_buf size enough or not */ txlen = txdesc_size + rtw_wlan_pkt_size(pxmitframe); - if( !pxmitbuf || + if (!pxmitbuf || ((_RND(pxmitbuf->len, 8) + txlen) > max_xmit_len) || (k >= (rtw_hal_sdio_max_txoqt_free_space(padapter) - 1)) ) { diff --git a/drivers/staging/rtl8723bs/include/HalVerDef.h b/drivers/staging/rtl8723bs/include/HalVerDef.h index 160f34efbfd5..c548fb126683 100644 --- a/drivers/staging/rtl8723bs/include/HalVerDef.h +++ b/drivers/staging/rtl8723bs/include/HalVerDef.h @@ -20,7 +20,7 @@ typedef enum tag_HAL_IC_Type_Definition CHIP_8821 = 7, CHIP_8723B = 8, CHIP_8192E = 9, -}HAL_IC_TYPE_E; +} HAL_IC_TYPE_E; /* HAL_CHIP_TYPE_E */ typedef enum tag_HAL_CHIP_Type_Definition @@ -28,7 +28,7 @@ typedef enum tag_HAL_CHIP_Type_Definition TEST_CHIP = 0, NORMAL_CHIP = 1, FPGA = 2, -}HAL_CHIP_TYPE_E; +} HAL_CHIP_TYPE_E; /* HAL_CUT_VERSION_E */ typedef enum tag_HAL_Cut_Version_Definition @@ -44,7 +44,7 @@ typedef enum tag_HAL_Cut_Version_Definition I_CUT_VERSION = 8, J_CUT_VERSION = 9, K_CUT_VERSION = 10, -}HAL_CUT_VERSION_E; +} HAL_CUT_VERSION_E; /* HAL_Manufacturer */ typedef enum tag_HAL_Manufacturer_Version_Definition @@ -52,7 +52,7 @@ typedef enum tag_HAL_Manufacturer_Version_Definition CHIP_VENDOR_TSMC = 0, CHIP_VENDOR_UMC = 1, CHIP_VENDOR_SMIC = 2, -}HAL_VENDOR_E; +} HAL_VENDOR_E; typedef enum tag_HAL_RF_Type_Definition { @@ -64,7 +64,7 @@ typedef enum tag_HAL_RF_Type_Definition RF_TYPE_3T3R = 5, RF_TYPE_3T4R = 6, RF_TYPE_4T4R = 7, -}HAL_RF_TYPE_E; +} HAL_RF_TYPE_E; typedef struct tag_HAL_VERSION { @@ -74,14 +74,14 @@ typedef struct tag_HAL_VERSION HAL_VENDOR_E VendorType; HAL_RF_TYPE_E RFType; u8 ROMVer; -}HAL_VERSION,*PHAL_VERSION; +} HAL_VERSION, *PHAL_VERSION; /* VERSION_8192C VersionID; */ /* HAL_VERSION VersionID; */ /* Get element */ -#define GET_CVID_IC_TYPE(version) ((HAL_IC_TYPE_E)((version).ICType) ) -#define GET_CVID_CHIP_TYPE(version) ((HAL_CHIP_TYPE_E)((version).ChipType) ) +#define GET_CVID_IC_TYPE(version) ((HAL_IC_TYPE_E)((version).ICType)) +#define GET_CVID_CHIP_TYPE(version) ((HAL_CHIP_TYPE_E)((version).ChipType)) #define GET_CVID_RF_TYPE(version) ((HAL_RF_TYPE_E)((version).RFType)) #define GET_CVID_MANUFACTUER(version) ((HAL_VENDOR_E)((version).VendorType)) #define GET_CVID_CUT_VERSION(version) ((HAL_CUT_VERSION_E)((version).CUTVersion)) @@ -93,8 +93,8 @@ typedef struct tag_HAL_VERSION /* HAL_VERSION VersionID */ /* HAL_CHIP_TYPE_E */ -#define IS_TEST_CHIP(version) ((GET_CVID_CHIP_TYPE(version) ==TEST_CHIP)? true: false) -#define IS_NORMAL_CHIP(version) ((GET_CVID_CHIP_TYPE(version) ==NORMAL_CHIP)? true: false) +#define IS_TEST_CHIP(version) ((GET_CVID_CHIP_TYPE(version) == TEST_CHIP) ? true : false) +#define IS_NORMAL_CHIP(version) ((GET_CVID_CHIP_TYPE(version) == NORMAL_CHIP) ? true : false) /* HAL_CUT_VERSION_E */ #define IS_A_CUT(version) ((GET_CVID_CUT_VERSION(version) == A_CUT_VERSION) ? true : false) @@ -107,13 +107,13 @@ typedef struct tag_HAL_VERSION #define IS_K_CUT(version) ((GET_CVID_CUT_VERSION(version) == K_CUT_VERSION) ? true : false) /* HAL_VENDOR_E */ -#define IS_CHIP_VENDOR_TSMC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_TSMC)? true: false) -#define IS_CHIP_VENDOR_UMC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_UMC)? true: false) -#define IS_CHIP_VENDOR_SMIC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_SMIC)? true: false) +#define IS_CHIP_VENDOR_TSMC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_TSMC) ? true : false) +#define IS_CHIP_VENDOR_UMC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_UMC) ? true : false) +#define IS_CHIP_VENDOR_SMIC(version) ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_SMIC) ? true : false) /* HAL_RF_TYPE_E */ -#define IS_1T1R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T1R)? true : false) -#define IS_1T2R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T2R)? true : false) -#define IS_2T2R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_2T2R)? true : false) +#define IS_1T1R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T1R) ? true : false) +#define IS_1T2R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T2R) ? true : false) +#define IS_2T2R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_2T2R) ? true : false) #endif diff --git a/drivers/staging/rtl8723bs/include/cmd_osdep.h b/drivers/staging/rtl8723bs/include/cmd_osdep.h index d3af9f44ad59..5506f513dc01 100644 --- a/drivers/staging/rtl8723bs/include/cmd_osdep.h +++ b/drivers/staging/rtl8723bs/include/cmd_osdep.h @@ -10,8 +10,8 @@ int rtw_init_cmd_priv(struct cmd_priv *pcmdpriv); int rtw_init_evt_priv(struct evt_priv *pevtpriv); -extern void _rtw_free_evt_priv (struct evt_priv *pevtpriv); -extern void _rtw_free_cmd_priv (struct cmd_priv *pcmdpriv); +extern void _rtw_free_evt_priv(struct evt_priv *pevtpriv); +extern void _rtw_free_cmd_priv(struct cmd_priv *pcmdpriv); int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj); extern struct cmd_obj *_rtw_dequeue_cmd(struct __queue *queue); diff --git a/drivers/staging/rtl8723bs/include/drv_types.h b/drivers/staging/rtl8723bs/include/drv_types.h index 6ec9087f2eb1..dba75216cbfe 100644 --- a/drivers/staging/rtl8723bs/include/drv_types.h +++ b/drivers/staging/rtl8723bs/include/drv_types.h @@ -77,7 +77,7 @@ enum _NIC_VERSION { #define SPEC_DEV_ID_RF_CONFIG_2T2R BIT(4) #define SPEC_DEV_ID_ASSIGN_IFNAME BIT(5) -struct specific_device_id{ +struct specific_device_id { u32 flags; @@ -151,8 +151,8 @@ struct registry_priv u8 lowrate_two_xmit; - u8 rf_config ; - u8 low_power ; + u8 rf_config; + u8 low_power; u8 wifi_spec;/* !turbo_mode */ @@ -498,11 +498,11 @@ enum ADAPTER_TYPE { MAX_ADAPTER = 0xFF, }; -typedef enum _DRIVER_STATE{ +typedef enum _DRIVER_STATE { DRIVER_NORMAL = 0, DRIVER_DISAPPEAR = 1, DRIVER_REPLACE_DONGLE = 2, -}DRIVER_STATE; +} DRIVER_STATE; struct adapter { int DriverState;/* for disable driver using module, use dongle to replace module. */ diff --git a/drivers/staging/rtl8723bs/include/hal_com.h b/drivers/staging/rtl8723bs/include/hal_com.h index f5c3ce5da70c..a46626d0509a 100644 --- a/drivers/staging/rtl8723bs/include/hal_com.h +++ b/drivers/staging/rtl8723bs/include/hal_com.h @@ -111,54 +111,54 @@ #define DESC_RATEVHTSS4MCS9 0x53 #define HDATA_RATE(rate)\ -(rate ==DESC_RATE1M)?"CCK_1M":\ -(rate ==DESC_RATE2M)?"CCK_2M":\ -(rate ==DESC_RATE5_5M)?"CCK5_5M":\ -(rate ==DESC_RATE11M)?"CCK_11M":\ -(rate ==DESC_RATE6M)?"OFDM_6M":\ -(rate ==DESC_RATE9M)?"OFDM_9M":\ -(rate ==DESC_RATE12M)?"OFDM_12M":\ -(rate ==DESC_RATE18M)?"OFDM_18M":\ -(rate ==DESC_RATE24M)?"OFDM_24M":\ -(rate ==DESC_RATE36M)?"OFDM_36M":\ -(rate ==DESC_RATE48M)?"OFDM_48M":\ -(rate ==DESC_RATE54M)?"OFDM_54M":\ -(rate ==DESC_RATEMCS0)?"MCS0":\ -(rate ==DESC_RATEMCS1)?"MCS1":\ -(rate ==DESC_RATEMCS2)?"MCS2":\ -(rate ==DESC_RATEMCS3)?"MCS3":\ -(rate ==DESC_RATEMCS4)?"MCS4":\ -(rate ==DESC_RATEMCS5)?"MCS5":\ -(rate ==DESC_RATEMCS6)?"MCS6":\ -(rate ==DESC_RATEMCS7)?"MCS7":\ -(rate ==DESC_RATEMCS8)?"MCS8":\ -(rate ==DESC_RATEMCS9)?"MCS9":\ -(rate ==DESC_RATEMCS10)?"MCS10":\ -(rate ==DESC_RATEMCS11)?"MCS11":\ -(rate ==DESC_RATEMCS12)?"MCS12":\ -(rate ==DESC_RATEMCS13)?"MCS13":\ -(rate ==DESC_RATEMCS14)?"MCS14":\ -(rate ==DESC_RATEMCS15)?"MCS15":\ -(rate ==DESC_RATEVHTSS1MCS0)?"VHTSS1MCS0":\ -(rate ==DESC_RATEVHTSS1MCS1)?"VHTSS1MCS1":\ -(rate ==DESC_RATEVHTSS1MCS2)?"VHTSS1MCS2":\ -(rate ==DESC_RATEVHTSS1MCS3)?"VHTSS1MCS3":\ -(rate ==DESC_RATEVHTSS1MCS4)?"VHTSS1MCS4":\ -(rate ==DESC_RATEVHTSS1MCS5)?"VHTSS1MCS5":\ -(rate ==DESC_RATEVHTSS1MCS6)?"VHTSS1MCS6":\ -(rate ==DESC_RATEVHTSS1MCS7)?"VHTSS1MCS7":\ -(rate ==DESC_RATEVHTSS1MCS8)?"VHTSS1MCS8":\ -(rate ==DESC_RATEVHTSS1MCS9)?"VHTSS1MCS9":\ -(rate ==DESC_RATEVHTSS2MCS0)?"VHTSS2MCS0":\ -(rate ==DESC_RATEVHTSS2MCS1)?"VHTSS2MCS1":\ -(rate ==DESC_RATEVHTSS2MCS2)?"VHTSS2MCS2":\ -(rate ==DESC_RATEVHTSS2MCS3)?"VHTSS2MCS3":\ -(rate ==DESC_RATEVHTSS2MCS4)?"VHTSS2MCS4":\ -(rate ==DESC_RATEVHTSS2MCS5)?"VHTSS2MCS5":\ -(rate ==DESC_RATEVHTSS2MCS6)?"VHTSS2MCS6":\ -(rate ==DESC_RATEVHTSS2MCS7)?"VHTSS2MCS7":\ -(rate ==DESC_RATEVHTSS2MCS8)?"VHTSS2MCS8":\ -(rate ==DESC_RATEVHTSS2MCS9)?"VHTSS2MCS9":"UNKNOW" +(rate == DESC_RATE1M) ? "CCK_1M" : \ +(rate == DESC_RATE2M) ? "CCK_2M" : \ +(rate == DESC_RATE5_5M) ? "CCK5_5M" : \ +(rate == DESC_RATE11M) ? "CCK_11M" : \ +(rate == DESC_RATE6M) ? "OFDM_6M" : \ +(rate == DESC_RATE9M) ? "OFDM_9M" : \ +(rate == DESC_RATE12M) ? "OFDM_12M" : \ +(rate == DESC_RATE18M) ? "OFDM_18M" : \ +(rate == DESC_RATE24M) ? "OFDM_24M" : \ +(rate == DESC_RATE36M) ? "OFDM_36M" : \ +(rate == DESC_RATE48M) ? "OFDM_48M" : \ +(rate == DESC_RATE54M) ? "OFDM_54M" : \ +(rate == DESC_RATEMCS0) ? "MCS0" : \ +(rate == DESC_RATEMCS1) ? "MCS1" : \ +(rate == DESC_RATEMCS2) ? "MCS2" : \ +(rate == DESC_RATEMCS3) ? "MCS3" : \ +(rate == DESC_RATEMCS4) ? "MCS4" : \ +(rate == DESC_RATEMCS5) ? "MCS5" : \ +(rate == DESC_RATEMCS6) ? "MCS6" : \ +(rate == DESC_RATEMCS7) ? "MCS7" : \ +(rate == DESC_RATEMCS8) ? "MCS8" : \ +(rate == DESC_RATEMCS9) ? "MCS9" : \ +(rate == DESC_RATEMCS10) ? "MCS10" : \ +(rate == DESC_RATEMCS11) ? "MCS11" : \ +(rate == DESC_RATEMCS12) ? "MCS12" : \ +(rate == DESC_RATEMCS13) ? "MCS13" : \ +(rate == DESC_RATEMCS14) ? "MCS14" : \ +(rate == DESC_RATEMCS15) ? "MCS15" : \ +(rate == DESC_RATEVHTSS1MCS0) ? "VHTSS1MCS0" : \ +(rate == DESC_RATEVHTSS1MCS1) ? "VHTSS1MCS1" : \ +(rate == DESC_RATEVHTSS1MCS2) ? "VHTSS1MCS2" : \ +(rate == DESC_RATEVHTSS1MCS3) ? "VHTSS1MCS3" : \ +(rate == DESC_RATEVHTSS1MCS4) ? "VHTSS1MCS4" : \ +(rate == DESC_RATEVHTSS1MCS5) ? "VHTSS1MCS5" : \ +(rate == DESC_RATEVHTSS1MCS6) ? "VHTSS1MCS6" : \ +(rate == DESC_RATEVHTSS1MCS7) ? "VHTSS1MCS7" : \ +(rate == DESC_RATEVHTSS1MCS8) ? "VHTSS1MCS8" : \ +(rate == DESC_RATEVHTSS1MCS9) ? "VHTSS1MCS9" : \ +(rate == DESC_RATEVHTSS2MCS0) ? "VHTSS2MCS0" : \ +(rate == DESC_RATEVHTSS2MCS1) ? "VHTSS2MCS1" : \ +(rate == DESC_RATEVHTSS2MCS2) ? "VHTSS2MCS2" : \ +(rate == DESC_RATEVHTSS2MCS3) ? "VHTSS2MCS3" : \ +(rate == DESC_RATEVHTSS2MCS4) ? "VHTSS2MCS4" : \ +(rate == DESC_RATEVHTSS2MCS5) ? "VHTSS2MCS5" : \ +(rate == DESC_RATEVHTSS2MCS6) ? "VHTSS2MCS6" : \ +(rate == DESC_RATEVHTSS2MCS7) ? "VHTSS2MCS7" : \ +(rate == DESC_RATEVHTSS2MCS8) ? "VHTSS2MCS8" : \ +(rate == DESC_RATEVHTSS2MCS9) ? "VHTSS2MCS9" : "UNKNOW" enum{ @@ -235,7 +235,7 @@ s32 c2h_evt_read_88xx(struct adapter *adapter, u8 *buf); u8 rtw_get_mgntframe_raid(struct adapter *adapter, unsigned char network_type); void rtw_hal_update_sta_rate_mask(struct adapter *padapter, struct sta_info *psta); -void hw_var_port_switch (struct adapter *adapter); +void hw_var_port_switch(struct adapter *adapter); void SetHwReg(struct adapter *padapter, u8 variable, u8 *val); void GetHwReg(struct adapter *padapter, u8 variable, u8 *val); diff --git a/drivers/staging/rtl8723bs/include/hal_com_h2c.h b/drivers/staging/rtl8723bs/include/hal_com_h2c.h index 7dbae5e2050e..b951bc288b89 100644 --- a/drivers/staging/rtl8723bs/include/hal_com_h2c.h +++ b/drivers/staging/rtl8723bs/include/hal_com_h2c.h @@ -11,7 +11,7 @@ /* H2C CMD DEFINITION ------------------------------------------------ */ /* */ /* 88e, 8723b, 8812, 8821, 92e use the same FW code base */ -enum h2c_cmd{ +enum h2c_cmd { /* Common Class: 000 */ H2C_RSVD_PAGE = 0x00, H2C_MEDIA_STATUS_RPT = 0x01, @@ -96,9 +96,9 @@ enum h2c_cmd{ #define H2C_PROBERSP_RSVDPAGE_LEN 5 #ifdef CONFIG_WOWLAN -#define eqMacAddr(a, b) (((a)[0]==(b)[0] && (a)[1]==(b)[1] && (a)[2]==(b)[2] && (a)[3]==(b)[3] && (a)[4]==(b)[4] && (a)[5]==(b)[5]) ? 1:0) -#define cpMacAddr(des, src) ((des)[0]=(src)[0], (des)[1]=(src)[1], (des)[2]=(src)[2], (des)[3]=(src)[3], (des)[4]=(src)[4], (des)[5]=(src)[5]) -#define cpIpAddr(des, src) ((des)[0]=(src)[0], (des)[1]=(src)[1], (des)[2]=(src)[2], (des)[3]=(src)[3]) +#define eqMacAddr(a, b) (((a)[0] == (b)[0] && (a)[1] == (b)[1] && (a)[2] == (b)[2] && (a)[3] == (b)[3] && (a)[4] == (b)[4] && (a)[5] == (b)[5]) ? 1 : 0) +#define cpMacAddr(des, src) ((des)[0] = (src)[0], (des)[1] = (src)[1], (des)[2] = (src)[2], (des)[3] = (src)[3], (des)[4] = (src)[4], (des)[5] = (src)[5]) +#define cpIpAddr(des, src) ((des)[0] = (src)[0], (des)[1] = (src)[1], (des)[2] = (src)[2], (des)[3] = (src)[3]) /* */ /* ARP packet */ diff --git a/drivers/staging/rtl8723bs/include/hal_com_phycfg.h b/drivers/staging/rtl8723bs/include/hal_com_phycfg.h index e9a3006a3e20..9fff4aa36546 100644 --- a/drivers/staging/rtl8723bs/include/hal_com_phycfg.h +++ b/drivers/staging/rtl8723bs/include/hal_com_phycfg.h @@ -177,7 +177,7 @@ u8 Channel, bool *bIn24G ); -s8 phy_get_tx_pwr_lmt (struct adapter *adapter, u32 RegPwrTblSel, +s8 phy_get_tx_pwr_lmt(struct adapter *adapter, u32 RegPwrTblSel, enum BAND_TYPE Band, enum CHANNEL_WIDTH Bandwidth, u8 RfPath, u8 DataRate, diff --git a/drivers/staging/rtl8723bs/include/hal_com_reg.h b/drivers/staging/rtl8723bs/include/hal_com_reg.h index 31a187b35810..37fa59a352d6 100644 --- a/drivers/staging/rtl8723bs/include/hal_com_reg.h +++ b/drivers/staging/rtl8723bs/include/hal_com_reg.h @@ -707,13 +707,13 @@ Default: 00b. /* ALL CCK Rate */ -#define RATE_ALL_CCK RATR_1M|RATR_2M|RATR_55M|RATR_11M -#define RATE_ALL_OFDM_AG RATR_6M|RATR_9M|RATR_12M|RATR_18M|RATR_24M|\ - RATR_36M|RATR_48M|RATR_54M -#define RATE_ALL_OFDM_1SS RATR_MCS0|RATR_MCS1|RATR_MCS2|RATR_MCS3 |\ - RATR_MCS4|RATR_MCS5|RATR_MCS6 |RATR_MCS7 -#define RATE_ALL_OFDM_2SS RATR_MCS8|RATR_MCS9 |RATR_MCS10|RATR_MCS11|\ - RATR_MCS12|RATR_MCS13|RATR_MCS14|RATR_MCS15 +#define RATE_ALL_CCK RATR_1M | RATR_2M | RATR_55M | RATR_11M +#define RATE_ALL_OFDM_AG RATR_6M | RATR_9M | RATR_12M | RATR_18M | RATR_24M |\ + RATR_36M | RATR_48M | RATR_54M +#define RATE_ALL_OFDM_1SS RATR_MCS0 | RATR_MCS1 | RATR_MCS2 | RATR_MCS3 |\ + RATR_MCS4 | RATR_MCS5 | RATR_MCS6 | RATR_MCS7 +#define RATE_ALL_OFDM_2SS RATR_MCS8 | RATR_MCS9 | RATR_MCS10 | RATR_MCS11 |\ + RATR_MCS12 | RATR_MCS13 | RATR_MCS14 | RATR_MCS15 #define RATE_BITMAP_ALL 0xFFFFF diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h index 24926ebaf950..1de5acaef8ff 100644 --- a/drivers/staging/rtl8723bs/include/hal_intf.h +++ b/drivers/staging/rtl8723bs/include/hal_intf.h @@ -296,7 +296,7 @@ enum wowlan_subcode { WOWLAN_AP_DISABLE = 13 }; -struct wowlan_ioctl_param{ +struct wowlan_ioctl_param { unsigned int subcode; unsigned int subcode_value; unsigned int wakeup_reason; diff --git a/drivers/staging/rtl8723bs/include/hal_phy.h b/drivers/staging/rtl8723bs/include/hal_phy.h index c6b9bf139ef6..ed0caa0574e3 100644 --- a/drivers/staging/rtl8723bs/include/hal_phy.h +++ b/drivers/staging/rtl8723bs/include/hal_phy.h @@ -25,7 +25,7 @@ #define RF6052_MAX_REG_92C 0x7F #define RF6052_MAX_REG \ - (RF6052_MAX_REG_88E > RF6052_MAX_REG_92C) ? RF6052_MAX_REG_88E: RF6052_MAX_REG_92C + (RF6052_MAX_REG_88E > RF6052_MAX_REG_92C) ? RF6052_MAX_REG_88E : RF6052_MAX_REG_92C #define GET_RF6052_REAL_MAX_REG(_Adapter) RF6052_MAX_REG_92C diff --git a/drivers/staging/rtl8723bs/include/hal_phy_cfg.h b/drivers/staging/rtl8723bs/include/hal_phy_cfg.h index b40868b2e76f..419ddb0733aa 100644 --- a/drivers/staging/rtl8723bs/include/hal_phy_cfg.h +++ b/drivers/staging/rtl8723bs/include/hal_phy_cfg.h @@ -58,9 +58,9 @@ u32 Data ); /* MAC/BB/RF HAL config */ -int PHY_BBConfig8723B(struct adapter *Adapter ); +int PHY_BBConfig8723B(struct adapter *Adapter); -int PHY_RFConfig8723B(struct adapter *Adapter ); +int PHY_RFConfig8723B(struct adapter *Adapter); s32 PHY_MACConfig8723B(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/include/hal_pwr_seq.h b/drivers/staging/rtl8723bs/include/hal_pwr_seq.h index 130a94879805..28aca047dce6 100644 --- a/drivers/staging/rtl8723bs/include/hal_pwr_seq.h +++ b/drivers/staging/rtl8723bs/include/hal_pwr_seq.h @@ -46,9 +46,9 @@ {0x0001, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_DELAY, 1, PWRSEQ_DELAY_MS},/*Delay 1ms*/ \ {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT5, 0}, /*0x00[5] = 1b'0 release analog Ips to digital , 1:isolation*/ \ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT4|BIT3|BIT2), 0},/* disable SW LPS 0x04[10]= 0 and WLSUS_EN 0x04[11]= 0*/ \ - {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0 , BIT0},/* Disable USB suspend */ \ + {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/* Disable USB suspend */ \ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT1, BIT1},/* wait till 0x04[17] = 1 power ready*/ \ - {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0 , 0},/* Enable USB suspend */ \ + {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0},/* Enable USB suspend */ \ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0},/* release WLON reset 0x04[16]= 1*/ \ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, 0},/* disable HWPDN 0x04[15]= 0*/ \ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT4|BIT3), 0},/* disable WL suspend*/ \ diff --git a/drivers/staging/rtl8723bs/include/ieee80211.h b/drivers/staging/rtl8723bs/include/ieee80211.h index 2110552b8e59..7243e656d589 100644 --- a/drivers/staging/rtl8723bs/include/ieee80211.h +++ b/drivers/staging/rtl8723bs/include/ieee80211.h @@ -98,8 +98,8 @@ enum { #define WPA_SELECTOR_LEN 4 -extern u8 RTW_WPA_OUI_TYPE[] ; -extern u16 RTW_WPA_VERSION ; +extern u8 RTW_WPA_OUI_TYPE[]; +extern u16 RTW_WPA_VERSION; extern u8 WPA_AUTH_KEY_MGMT_NONE[]; extern u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[]; extern u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[]; @@ -139,7 +139,7 @@ typedef enum _RATEID_IDX_ { RATEID_IDX_VHT_1SS = 10, } RATEID_IDX, *PRATEID_IDX; -typedef enum _RATR_TABLE_MODE{ +typedef enum _RATR_TABLE_MODE { RATR_INX_WIRELESS_NGB = 0, /* BGN 40 Mhz 2SS 1SS */ RATR_INX_WIRELESS_NG = 1, /* GN or N */ RATR_INX_WIRELESS_NB = 2, /* BGN 20 Mhz 2SS 1SS or BN */ @@ -149,7 +149,7 @@ typedef enum _RATR_TABLE_MODE{ RATR_INX_WIRELESS_B = 6, RATR_INX_WIRELESS_MC = 7, RATR_INX_WIRELESS_AC_N = 8, -}RATR_TABLE_MODE, *PRATR_TABLE_MODE; +} RATR_TABLE_MODE, *PRATR_TABLE_MODE; enum NETWORK_TYPE @@ -248,7 +248,7 @@ struct ieee_param_ex { u8 data[0]; }; -struct sta_data{ +struct sta_data { u16 aid; u16 capability; int flags; @@ -439,7 +439,7 @@ struct ieee80211_snap_hdr { #define IEEE80211_OFDM_SHIFT_MASK_A 4 -enum MGN_RATE{ +enum MGN_RATE { MGN_1M = 0x02, MGN_2M = 0x04, MGN_5_5M = 0x0B, @@ -906,7 +906,7 @@ enum rtw_ieee80211_spectrum_mgmt_actioncode { RTW_WLAN_ACTION_SPCT_EXT_CHL_SWITCH = 5, }; -enum _PUBLIC_ACTION{ +enum _PUBLIC_ACTION { ACT_PUBLIC_BSSCOEXIST = 0, /* 20/40 BSS Coexistence */ ACT_PUBLIC_DSE_ENABLE = 1, ACT_PUBLIC_DSE_DEENABLE = 2, @@ -953,7 +953,7 @@ enum rtw_ieee80211_back_parties { }; /* VHT features action code */ -enum rtw_ieee80211_vht_actioncode{ +enum rtw_ieee80211_vht_actioncode { RTW_WLAN_ACTION_VHT_COMPRESSED_BEAMFORMING = 0, RTW_WLAN_ACTION_VHT_GROUPID_MANAGEMENT = 1, RTW_WLAN_ACTION_VHT_OPMODE_NOTIFICATION = 2, @@ -1128,7 +1128,7 @@ u8 *rtw_get_ie(u8*pbuf, sint index, sint *len, sint limit); u8 *rtw_get_ie_ex(u8 *in_ie, uint in_len, u8 eid, u8 *oui, u8 oui_len, u8 *ie, uint *ielen); int rtw_ies_remove_ie(u8 *ies, uint *ies_len, uint offset, u8 eid, u8 *oui, u8 oui_len); -void rtw_set_supported_rate(u8 *SupportedRates, uint mode) ; +void rtw_set_supported_rate(u8 *SupportedRates, uint mode); unsigned char *rtw_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit); unsigned char *rtw_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit); @@ -1142,8 +1142,8 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie u8 rtw_is_wps_ie(u8 *ie_ptr, uint *wps_ielen); u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen); -u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id , u8 *buf_attr, u32 *len_attr); -u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id , u8 *buf_content, uint *len_content); +u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_attr, u32 *len_attr); +u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_content, uint *len_content); /** * for_each_ie - iterate over continuous IEs diff --git a/drivers/staging/rtl8723bs/include/osdep_intf.h b/drivers/staging/rtl8723bs/include/osdep_intf.h index fa16139fcce6..c59c1384944b 100644 --- a/drivers/staging/rtl8723bs/include/osdep_intf.h +++ b/drivers/staging/rtl8723bs/include/osdep_intf.h @@ -50,7 +50,7 @@ void rtw_reset_drv_sw(struct adapter *padapter); void rtw_dev_unload(struct adapter *padapter); u32 rtw_start_drv_threads(struct adapter *padapter); -void rtw_stop_drv_threads (struct adapter *padapter); +void rtw_stop_drv_threads(struct adapter *padapter); void rtw_cancel_all_timer(struct adapter *padapter); int rtw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); diff --git a/drivers/staging/rtl8723bs/include/osdep_service.h b/drivers/staging/rtl8723bs/include/osdep_service.h index a40cf7b60a69..5f681899bbec 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service.h +++ b/drivers/staging/rtl8723bs/include/osdep_service.h @@ -80,7 +80,7 @@ enum mstat_f { #define mstat_tf_idx(flags) ((flags)&0xff) #define mstat_ff_idx(flags) (((flags)&0xff00) >> 8) -typedef enum mstat_status{ +typedef enum mstat_status { MSTAT_ALLOC_SUCCESS = 0, MSTAT_ALLOC_FAIL, MSTAT_FREE @@ -117,7 +117,7 @@ static inline void thread_enter(char *name) static inline void flush_signals_thread(void) { - if (signal_pending (current)) + if (signal_pending(current)) { flush_signals(current); } @@ -134,14 +134,14 @@ static inline int rtw_bug_check(void *parg1, void *parg2, void *parg3, void *par } #define _RND(sz, r) ((((sz)+((r)-1))/(r))*(r)) -#define RND4(x) (((x >> 2) + (((x & 3) == 0) ? 0: 1)) << 2) +#define RND4(x) (((x >> 2) + (((x & 3) == 0) ? 0 : 1)) << 2) static inline u32 _RND4(u32 sz) { u32 val; - val = ((sz >> 2) + ((sz & 3) ? 1: 0)) << 2; + val = ((sz >> 2) + ((sz & 3) ? 1 : 0)) << 2; return val; @@ -152,7 +152,7 @@ static inline u32 _RND8(u32 sz) u32 val; - val = ((sz >> 3) + ((sz & 7) ? 1: 0)) << 3; + val = ((sz >> 3) + ((sz & 7) ? 1 : 0)) << 3; return val; diff --git a/drivers/staging/rtl8723bs/include/osdep_service_linux.h b/drivers/staging/rtl8723bs/include/osdep_service_linux.h index a2d9de866c4b..1710fa3eeb71 100644 --- a/drivers/staging/rtl8723bs/include/osdep_service_linux.h +++ b/drivers/staging/rtl8723bs/include/osdep_service_linux.h @@ -80,7 +80,7 @@ static inline struct list_head *get_list_head(struct __queue *queue) static inline void _set_timer(_timer *ptimer, u32 delay_time) { - mod_timer(ptimer , (jiffies+(delay_time*HZ/1000))); + mod_timer(ptimer, (jiffies + (delay_time * HZ / 1000))); } static inline void _cancel_timer(_timer *ptimer, u8 *bcancelled) diff --git a/drivers/staging/rtl8723bs/include/recv_osdep.h b/drivers/staging/rtl8723bs/include/recv_osdep.h index 1056f615d0f9..e85aafc93f6d 100644 --- a/drivers/staging/rtl8723bs/include/recv_osdep.h +++ b/drivers/staging/rtl8723bs/include/recv_osdep.h @@ -9,7 +9,7 @@ extern sint _rtw_init_recv_priv(struct recv_priv *precvpriv, struct adapter *padapter); -extern void _rtw_free_recv_priv (struct recv_priv *precvpriv); +extern void _rtw_free_recv_priv(struct recv_priv *precvpriv); extern s32 rtw_recv_entry(union recv_frame *precv_frame); @@ -19,7 +19,7 @@ extern void rtw_recv_returnpacket(_nic_hdl cnxt, _pkt *preturnedpkt); extern void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup); int rtw_init_recv_priv(struct recv_priv *precvpriv, struct adapter *padapter); -void rtw_free_recv_priv (struct recv_priv *precvpriv); +void rtw_free_recv_priv(struct recv_priv *precvpriv); void rtw_os_recv_resource_alloc(struct adapter *padapter, union recv_frame *precvframe); diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_cmd.h b/drivers/staging/rtl8723bs/include/rtl8723b_cmd.h index ecfd2d383ac2..3bfb0e9be582 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_cmd.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_cmd.h @@ -11,7 +11,7 @@ /* H2C CMD DEFINITION ------------------------------------------------ */ /* */ -enum h2c_cmd_8723B{ +enum h2c_cmd_8723B { /* Common Class: 000 */ H2C_8723B_RSVD_PAGE = 0x00, H2C_8723B_MEDIA_STATUS_RPT = 0x01, diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_rf.h b/drivers/staging/rtl8723bs/include/rtl8723b_rf.h index 987b9f12a4f4..d712c6d36a08 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_rf.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_rf.h @@ -8,7 +8,7 @@ #define __RTL8723B_RF_H__ -int PHY_RF6052_Config8723B(struct adapter *Adapter ); +int PHY_RF6052_Config8723B(struct adapter *Adapter); void PHY_RF6052SetBandwidth8723B(struct adapter *Adapter, diff --git a/drivers/staging/rtl8723bs/include/rtl8723b_xmit.h b/drivers/staging/rtl8723bs/include/rtl8723b_xmit.h index 23a6069c3e5d..320ca65e5faa 100644 --- a/drivers/staging/rtl8723bs/include/rtl8723b_xmit.h +++ b/drivers/staging/rtl8723bs/include/rtl8723b_xmit.h @@ -176,7 +176,7 @@ typedef struct txdesc_8723b u32 txbf_path:1; u32 seq:12; u32 final_data_rate:8; -}TXDESC_8723B, *PTXDESC_8723B; +} TXDESC_8723B, *PTXDESC_8723B; #ifndef __INC_HAL8723BDESC_H #define __INC_HAL8723BDESC_H diff --git a/drivers/staging/rtl8723bs/include/rtw_byteorder.h b/drivers/staging/rtl8723bs/include/rtw_byteorder.h index f76fbfbed4c7..c3a65f971793 100644 --- a/drivers/staging/rtl8723bs/include/rtw_byteorder.h +++ b/drivers/staging/rtl8723bs/include/rtw_byteorder.h @@ -7,7 +7,7 @@ #ifndef _RTL871X_BYTEORDER_H_ #define _RTL871X_BYTEORDER_H_ -#if defined (__LITTLE_ENDIAN) +#if defined(__LITTLE_ENDIAN) #include #else # include diff --git a/drivers/staging/rtl8723bs/include/rtw_cmd.h b/drivers/staging/rtl8723bs/include/rtw_cmd.h index b83824ca2e31..3e025a652e38 100644 --- a/drivers/staging/rtl8723bs/include/rtw_cmd.h +++ b/drivers/staging/rtl8723bs/include/rtw_cmd.h @@ -75,7 +75,7 @@ do {\ INIT_LIST_HEAD(&pcmd->list);\ pcmd->cmdcode = code;\ pcmd->parmbuf = (u8 *)(pparm);\ - pcmd->cmdsz = sizeof (*pparm);\ + pcmd->cmdsz = sizeof(*pparm);\ pcmd->rsp = NULL;\ pcmd->rspsz = 0;\ } while (0) @@ -129,9 +129,9 @@ extern void rtw_free_cmd_obj(struct cmd_obj *pcmd); void rtw_stop_cmd_thread(struct adapter *adapter); int rtw_cmd_thread(void *context); -extern void rtw_free_cmd_priv (struct cmd_priv *pcmdpriv); +extern void rtw_free_cmd_priv(struct cmd_priv *pcmdpriv); -extern void rtw_free_evt_priv (struct evt_priv *pevtpriv); +extern void rtw_free_evt_priv(struct evt_priv *pevtpriv); extern void rtw_evt_notify_isr(struct evt_priv *pevtpriv); enum rtw_drvextra_cmd_id @@ -163,10 +163,10 @@ enum LPS_CTRL_TYPE { LPS_CTRL_SCAN = 0, LPS_CTRL_JOINBSS = 1, - LPS_CTRL_CONNECT =2, - LPS_CTRL_DISCONNECT =3, - LPS_CTRL_SPECIAL_PACKET =4, - LPS_CTRL_LEAVE =5, + LPS_CTRL_CONNECT = 2, + LPS_CTRL_DISCONNECT = 3, + LPS_CTRL_SPECIAL_PACKET = 4, + LPS_CTRL_LEAVE = 5, LPS_CTRL_TRAFFIC_BUSY = 6, }; @@ -692,40 +692,40 @@ struct getratable_rsp { /* to get TX, RX retry count */ -struct gettxretrycnt_parm{ +struct gettxretrycnt_parm { unsigned int rsvd; }; -struct gettxretrycnt_rsp{ +struct gettxretrycnt_rsp { unsigned long tx_retrycnt; }; -struct getrxretrycnt_parm{ +struct getrxretrycnt_parm { unsigned int rsvd; }; -struct getrxretrycnt_rsp{ +struct getrxretrycnt_rsp { unsigned long rx_retrycnt; }; /* to get BCNOK, BCNERR count */ -struct getbcnokcnt_parm{ +struct getbcnokcnt_parm { unsigned int rsvd; }; -struct getbcnokcnt_rsp{ +struct getbcnokcnt_rsp { unsigned long bcnokcnt; }; -struct getbcnerrcnt_parm{ +struct getbcnerrcnt_parm { unsigned int rsvd; }; -struct getbcnerrcnt_rsp{ +struct getbcnerrcnt_rsp { unsigned long bcnerrcnt; }; /* to get current TX power level */ -struct getcurtxpwrlevel_parm{ +struct getcurtxpwrlevel_parm { unsigned int rsvd; }; -struct getcurtxpwrlevel_rsp{ +struct getcurtxpwrlevel_rsp { unsigned short tx_power; }; @@ -883,56 +883,56 @@ struct _cmd_callback { enum rtw_h2c_cmd { - GEN_CMD_CODE(_Read_MACREG) , /*0*/ - GEN_CMD_CODE(_Write_MACREG) , - GEN_CMD_CODE(_Read_BBREG) , - GEN_CMD_CODE(_Write_BBREG) , - GEN_CMD_CODE(_Read_RFREG) , - GEN_CMD_CODE(_Write_RFREG) , /*5*/ - GEN_CMD_CODE(_Read_EEPROM) , - GEN_CMD_CODE(_Write_EEPROM) , - GEN_CMD_CODE(_Read_EFUSE) , - GEN_CMD_CODE(_Write_EFUSE) , + GEN_CMD_CODE(_Read_MACREG), /*0*/ + GEN_CMD_CODE(_Write_MACREG), + GEN_CMD_CODE(_Read_BBREG), + GEN_CMD_CODE(_Write_BBREG), + GEN_CMD_CODE(_Read_RFREG), + GEN_CMD_CODE(_Write_RFREG), /*5*/ + GEN_CMD_CODE(_Read_EEPROM), + GEN_CMD_CODE(_Write_EEPROM), + GEN_CMD_CODE(_Read_EFUSE), + GEN_CMD_CODE(_Write_EFUSE), - GEN_CMD_CODE(_Read_CAM) , /*10*/ - GEN_CMD_CODE(_Write_CAM) , + GEN_CMD_CODE(_Read_CAM), /*10*/ + GEN_CMD_CODE(_Write_CAM), GEN_CMD_CODE(_setBCNITV), GEN_CMD_CODE(_setMBIDCFG), GEN_CMD_CODE(_JoinBss), /*14*/ - GEN_CMD_CODE(_DisConnect) , /*15*/ - GEN_CMD_CODE(_CreateBss) , - GEN_CMD_CODE(_SetOpMode) , + GEN_CMD_CODE(_DisConnect), /*15*/ + GEN_CMD_CODE(_CreateBss), + GEN_CMD_CODE(_SetOpMode), GEN_CMD_CODE(_SiteSurvey), /*18*/ - GEN_CMD_CODE(_SetAuth) , + GEN_CMD_CODE(_SetAuth), - GEN_CMD_CODE(_SetKey) , /*20*/ - GEN_CMD_CODE(_SetStaKey) , - GEN_CMD_CODE(_SetAssocSta) , - GEN_CMD_CODE(_DelAssocSta) , - GEN_CMD_CODE(_SetStaPwrState) , - GEN_CMD_CODE(_SetBasicRate) , /*25*/ - GEN_CMD_CODE(_GetBasicRate) , - GEN_CMD_CODE(_SetDataRate) , - GEN_CMD_CODE(_GetDataRate) , - GEN_CMD_CODE(_SetPhyInfo) , + GEN_CMD_CODE(_SetKey), /*20*/ + GEN_CMD_CODE(_SetStaKey), + GEN_CMD_CODE(_SetAssocSta), + GEN_CMD_CODE(_DelAssocSta), + GEN_CMD_CODE(_SetStaPwrState), + GEN_CMD_CODE(_SetBasicRate), /*25*/ + GEN_CMD_CODE(_GetBasicRate), + GEN_CMD_CODE(_SetDataRate), + GEN_CMD_CODE(_GetDataRate), + GEN_CMD_CODE(_SetPhyInfo), - GEN_CMD_CODE(_GetPhyInfo) , /*30*/ - GEN_CMD_CODE(_SetPhy) , - GEN_CMD_CODE(_GetPhy) , - GEN_CMD_CODE(_readRssi) , - GEN_CMD_CODE(_readGain) , - GEN_CMD_CODE(_SetAtim) , /*35*/ - GEN_CMD_CODE(_SetPwrMode) , + GEN_CMD_CODE(_GetPhyInfo), /*30*/ + GEN_CMD_CODE(_SetPhy), + GEN_CMD_CODE(_GetPhy), + GEN_CMD_CODE(_readRssi), + GEN_CMD_CODE(_readGain), + GEN_CMD_CODE(_SetAtim), /*35*/ + GEN_CMD_CODE(_SetPwrMode), GEN_CMD_CODE(_JoinbssRpt), - GEN_CMD_CODE(_SetRaTable) , - GEN_CMD_CODE(_GetRaTable) , + GEN_CMD_CODE(_SetRaTable), + GEN_CMD_CODE(_GetRaTable), GEN_CMD_CODE(_GetCCXReport), /*40*/ GEN_CMD_CODE(_GetDTMReport), GEN_CMD_CODE(_GetTXRateStatistics), GEN_CMD_CODE(_SetUsbSuspend), GEN_CMD_CODE(_SetH2cLbk), - GEN_CMD_CODE(_AddBAReq) , /*45*/ + GEN_CMD_CODE(_AddBAReq), /*45*/ GEN_CMD_CODE(_SetChannel), /*46*/ GEN_CMD_CODE(_SetTxPower), GEN_CMD_CODE(_SwitchAntenna), diff --git a/drivers/staging/rtl8723bs/include/rtw_debug.h b/drivers/staging/rtl8723bs/include/rtw_debug.h index 22fc5d730d7b..c90adfb87261 100644 --- a/drivers/staging/rtl8723bs/include/rtw_debug.h +++ b/drivers/staging/rtl8723bs/include/rtw_debug.h @@ -131,13 +131,13 @@ #define _MODULE_DEFINE_ _module_efuse_ #endif -#define RT_TRACE(_Comp, _Level, Fmt) do{}while (0) -#define RT_PRINT_DATA(_Comp, _Level, _TitleString, _HexData, _HexDataLen) do{}while (0) +#define RT_TRACE(_Comp, _Level, Fmt) do {} while (0) +#define RT_PRINT_DATA(_Comp, _Level, _TitleString, _HexData, _HexDataLen) do {} while (0) #define DBG_871X(x, ...) do {} while (0) #define MSG_8192C(x, ...) do {} while (0) -#define DBG_8192C(x,...) do {} while (0) -#define DBG_871X_LEVEL(x,...) do {} while (0) +#define DBG_8192C(x, ...) do {} while (0) +#define DBG_871X_LEVEL(x, ...) do {} while (0) #undef _dbgdump @@ -161,8 +161,8 @@ _dbgdump(DRIVER_PREFIX"ERROR " fmt, ##arg);\ else \ _dbgdump(DRIVER_PREFIX fmt, ##arg);\ - }\ - }while (0) + } \ + } while (0) /* without driver-defined prefix */ #undef _DBG_871X_LEVEL @@ -173,8 +173,8 @@ _dbgdump("ERROR " fmt, ##arg);\ else \ _dbgdump(fmt, ##arg);\ - }\ - }while (0) + } \ + } while (0) #define RTW_DBGDUMP NULL /* 'stream' for _dbgdump */ @@ -203,17 +203,17 @@ #undef DBG_871X #define DBG_871X(...) do {\ _dbgdump(DRIVER_PREFIX __VA_ARGS__);\ - }while (0) + } while (0) #undef MSG_8192C #define MSG_8192C(...) do {\ _dbgdump(DRIVER_PREFIX __VA_ARGS__);\ - }while (0) + } while (0) #undef DBG_8192C #define DBG_8192C(...) do {\ _dbgdump(DRIVER_PREFIX __VA_ARGS__);\ - }while (0) + } while (0) #endif /* defined(_dbgdump) */ #endif /* DEBUG */ @@ -227,8 +227,8 @@ if ((_Comp & GlobalDebugComponents) && (_Level <= GlobalDebugLevel)) {\ _dbgdump("%s [0x%08x,%d]", DRIVER_PREFIX, (unsigned int)_Comp, _Level);\ _dbgdump Fmt;\ - }\ - }while (0) + } \ + } while (0) #endif /* defined(_dbgdump) && defined(_MODULE_DEFINE_) */ @@ -242,7 +242,7 @@ u8 *ptr = (u8 *)_HexData; \ _dbgdump("%s", DRIVER_PREFIX); \ _dbgdump(_TitleString); \ - for (__i = 0; __i<(int)_HexDataLen; __i++) \ + for (__i = 0; __i < (int)_HexDataLen; __i++) \ { \ _dbgdump("%02X%s", ptr[__i], (((__i + 1) % 4) == 0)?" ":" "); \ if (((__i + 1) % 16) == 0) _dbgdump("\n"); \ diff --git a/drivers/staging/rtl8723bs/include/rtw_eeprom.h b/drivers/staging/rtl8723bs/include/rtw_eeprom.h index 1fcd79fa1c21..704c6461333a 100644 --- a/drivers/staging/rtl8723bs/include/rtw_eeprom.h +++ b/drivers/staging/rtl8723bs/include/rtw_eeprom.h @@ -91,7 +91,7 @@ typedef enum _RT_CUSTOMER_ID RT_CID_819x_ALPHA_Dlink = 44,/* add by ylb 20121012 for customer led for alpha */ RT_CID_WNC_NEC = 45,/* add by page for NEC */ RT_CID_DNI_BUFFALO = 46,/* add by page for NEC */ -}RT_CUSTOMER_ID, *PRT_CUSTOMER_ID; +} RT_CUSTOMER_ID, *PRT_CUSTOMER_ID; struct eeprom_priv { diff --git a/drivers/staging/rtl8723bs/include/rtw_efuse.h b/drivers/staging/rtl8723bs/include/rtw_efuse.h index 9d9a5a3e336d..4abcbbc8f513 100644 --- a/drivers/staging/rtl8723bs/include/rtw_efuse.h +++ b/drivers/staging/rtl8723bs/include/rtw_efuse.h @@ -57,15 +57,15 @@ enum _EFUSE_DEF_TYPE { #define EFUSE_MAX_WORD_UNIT 4 /*------------------------------Define structure----------------------------*/ -typedef struct PG_PKT_STRUCT_A{ +typedef struct PG_PKT_STRUCT_A { u8 offset; u8 word_en; u8 data[8]; u8 word_cnts; -}PGPKT_STRUCT,*PPGPKT_STRUCT; +} PGPKT_STRUCT, *PPGPKT_STRUCT; /*------------------------------Define structure----------------------------*/ -typedef struct _EFUSE_HAL{ +typedef struct _EFUSE_HAL { u8 fakeEfuseBank; u32 fakeEfuseUsedBytes; u8 fakeEfuseContent[EFUSE_MAX_HW_SIZE]; @@ -82,7 +82,7 @@ typedef struct _EFUSE_HAL{ u8 fakeBTEfuseContent[EFUSE_MAX_BT_BANK][EFUSE_MAX_HW_SIZE]; u8 fakeBTEfuseInitMap[EFUSE_BT_MAX_MAP_LEN]; u8 fakeBTEfuseModifiedMap[EFUSE_BT_MAX_MAP_LEN]; -}EFUSE_HAL, *PEFUSE_HAL; +} EFUSE_HAL, *PEFUSE_HAL; /*------------------------Export global variable----------------------------*/ diff --git a/drivers/staging/rtl8723bs/include/rtw_event.h b/drivers/staging/rtl8723bs/include/rtw_event.h index ee80aa21eb10..aeaabab780e5 100644 --- a/drivers/staging/rtl8723bs/include/rtw_event.h +++ b/drivers/staging/rtl8723bs/include/rtw_event.h @@ -82,7 +82,7 @@ struct fwevent { #define C2HEVENT_SZ 32 -struct event_node{ +struct event_node { unsigned char *node; unsigned char evt_code; unsigned short evt_sz; diff --git a/drivers/staging/rtl8723bs/include/rtw_ht.h b/drivers/staging/rtl8723bs/include/rtw_ht.h index 952c804dd0fd..4c224c128327 100644 --- a/drivers/staging/rtl8723bs/include/rtw_ht.h +++ b/drivers/staging/rtl8723bs/include/rtw_ht.h @@ -38,7 +38,7 @@ struct ht_priv }; -typedef enum AGGRE_SIZE{ +typedef enum AGGRE_SIZE { HT_AGG_SIZE_8K = 0, HT_AGG_SIZE_16K = 1, HT_AGG_SIZE_32K = 2, @@ -47,9 +47,9 @@ typedef enum AGGRE_SIZE{ VHT_AGG_SIZE_256K = 5, VHT_AGG_SIZE_512K = 6, VHT_AGG_SIZE_1024K = 7, -}AGGRE_SIZE_E, *PAGGRE_SIZE_E; +} AGGRE_SIZE_E, *PAGGRE_SIZE_E; -typedef enum _RT_HT_INF0_CAP{ +typedef enum _RT_HT_INF0_CAP { RT_HT_CAP_USE_TURBO_AGGR = 0x01, RT_HT_CAP_USE_LONG_PREAMBLE = 0x02, RT_HT_CAP_USE_AMPDU = 0x04, @@ -58,13 +58,13 @@ typedef enum _RT_HT_INF0_CAP{ RT_HT_CAP_USE_92SE = 0x20, RT_HT_CAP_USE_88C_92C = 0x40, RT_HT_CAP_USE_AP_CLIENT_MODE = 0x80, /* AP team request to reserve this bit, by Emily */ -}RT_HT_INF0_CAPBILITY, *PRT_HT_INF0_CAPBILITY; +} RT_HT_INF0_CAPBILITY, *PRT_HT_INF0_CAPBILITY; -typedef enum _RT_HT_INF1_CAP{ +typedef enum _RT_HT_INF1_CAP { RT_HT_CAP_USE_VIDEO_CLIENT = 0x01, RT_HT_CAP_USE_JAGUAR_BCUT = 0x02, RT_HT_CAP_USE_JAGUAR_CCUT = 0x04, -}RT_HT_INF1_CAPBILITY, *PRT_HT_INF1_CAPBILITY; +} RT_HT_INF1_CAPBILITY, *PRT_HT_INF1_CAPBILITY; #define LDPC_HT_ENABLE_RX BIT0 #define LDPC_HT_ENABLE_TX BIT1 diff --git a/drivers/staging/rtl8723bs/include/rtw_io.h b/drivers/staging/rtl8723bs/include/rtw_io.h index 99d104b3647a..2581b5165d1b 100644 --- a/drivers/staging/rtl8723bs/include/rtw_io.h +++ b/drivers/staging/rtl8723bs/include/rtw_io.h @@ -168,7 +168,7 @@ struct reg_protocol_rd { u32 Byte2Access : 1; u32 Byte1Access : 1; - u32 BurstMode :1 ; + u32 BurstMode :1; u32 FixOrContinuous : 1; u32 Reserved4 : 16; @@ -224,7 +224,7 @@ struct reg_protocol_wt { u32 Byte2Access : 1; u32 Byte1Access : 1; - u32 BurstMode :1 ; + u32 BurstMode :1; u32 FixOrContinuous : 1; u32 Reserved4 : 16; @@ -259,7 +259,7 @@ struct io_queue { struct intf_hdl intf; }; -struct io_priv{ +struct io_priv { struct adapter *padapter; diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme.h b/drivers/staging/rtl8723bs/include/rtw_mlme.h index 362737b83c3a..14e4bce28856 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme.h @@ -85,7 +85,7 @@ typedef enum _RT_SCAN_TYPE { SCAN_PASSIVE, SCAN_ACTIVE, SCAN_MIX, -}RT_SCAN_TYPE, *PRT_SCAN_TYPE; +} RT_SCAN_TYPE, *PRT_SCAN_TYPE; enum _BAND { GHZ24_50 = 0, @@ -135,7 +135,7 @@ struct sitesurvey_ctrl { _timer sitesurvey_ctrl_timer; }; -typedef struct _RT_LINK_DETECT_T{ +typedef struct _RT_LINK_DETECT_T { u32 NumTxOkInPeriod; u32 NumRxOkInPeriod; u32 NumRxUnicastOkInPeriod; @@ -148,62 +148,62 @@ typedef struct _RT_LINK_DETECT_T{ /* u8 TrafficBusyState; */ u8 TrafficTransitionCount; u32 LowPowerTransitionCount; -}RT_LINK_DETECT_T, *PRT_LINK_DETECT_T; +} RT_LINK_DETECT_T, *PRT_LINK_DETECT_T; struct profile_info { u8 ssidlen; - u8 ssid[ WLAN_SSID_MAXLEN ]; - u8 peermac[ ETH_ALEN ]; + u8 ssid[WLAN_SSID_MAXLEN]; + u8 peermac[ETH_ALEN]; }; -struct tx_invite_req_info{ +struct tx_invite_req_info { u8 token; u8 benable; - u8 go_ssid[ WLAN_SSID_MAXLEN ]; + u8 go_ssid[WLAN_SSID_MAXLEN]; u8 ssidlen; - u8 go_bssid[ ETH_ALEN ]; - u8 peer_macaddr[ ETH_ALEN ]; + u8 go_bssid[ETH_ALEN]; + u8 peer_macaddr[ETH_ALEN]; u8 operating_ch; /* This information will be set by using the p2p_set op_ch =x */ u8 peer_ch; /* The listen channel for peer P2P device */ }; -struct tx_invite_resp_info{ +struct tx_invite_resp_info { u8 token; /* Used to record the dialog token of p2p invitation request frame. */ }; -struct tx_provdisc_req_info{ +struct tx_provdisc_req_info { u16 wps_config_method_request; /* Used when sending the provisioning request frame */ u16 peer_channel_num[2]; /* The channel number which the receiver stands. */ struct ndis_802_11_ssid ssid; - u8 peerDevAddr[ ETH_ALEN ]; /* Peer device address */ - u8 peerIFAddr[ ETH_ALEN ]; /* Peer interface address */ + u8 peerDevAddr[ETH_ALEN]; /* Peer device address */ + u8 peerIFAddr[ETH_ALEN]; /* Peer interface address */ u8 benable; /* This provision discovery request frame is trigger to send or not */ }; -struct rx_provdisc_req_info{ /* When peer device issue prov_disc_req first, we should store the following informations */ - u8 peerDevAddr[ ETH_ALEN ]; /* Peer device address */ +struct rx_provdisc_req_info { /* When peer device issue prov_disc_req first, we should store the following informations */ + u8 peerDevAddr[ETH_ALEN]; /* Peer device address */ u8 strconfig_method_desc_of_prov_disc_req[4]; /* description for the config method located in the provisioning discovery request frame. */ /* The UI must know this information to know which config method the remote p2p device is requiring. */ }; -struct tx_nego_req_info{ +struct tx_nego_req_info { u16 peer_channel_num[2]; /* The channel number which the receiver stands. */ - u8 peerDevAddr[ ETH_ALEN ]; /* Peer device address */ + u8 peerDevAddr[ETH_ALEN]; /* Peer device address */ u8 benable; /* This negoitation request frame is trigger to send or not */ }; -struct group_id_info{ - u8 go_device_addr[ ETH_ALEN ]; /* The GO's device address of this P2P group */ - u8 ssid[ WLAN_SSID_MAXLEN ]; /* The SSID of this P2P group */ +struct group_id_info { + u8 go_device_addr[ETH_ALEN]; /* The GO's device address of this P2P group */ + u8 ssid[WLAN_SSID_MAXLEN]; /* The SSID of this P2P group */ }; -struct scan_limit_info{ +struct scan_limit_info { u8 scan_op_ch_only; /* When this flag is set, the driver should just scan the operation channel */ u8 operation_ch[2]; /* Store the operation channel of invitation request frame */ }; -struct cfg80211_wifidirect_info{ +struct cfg80211_wifidirect_info { _timer remain_on_ch_timer; u8 restore_channel; struct ieee80211_channel remain_on_ch_channel; @@ -213,7 +213,7 @@ struct cfg80211_wifidirect_info{ unsigned long last_ro_ch_time; /* this will be updated at the beginning and end of ro_ch */ }; -struct wifidirect_info{ +struct wifidirect_info { struct adapter * padapter; _timer find_phase_timer; _timer restore_p2p_state_timer; @@ -225,7 +225,7 @@ struct wifidirect_info{ struct tx_provdisc_req_info tx_prov_disc_info; struct rx_provdisc_req_info rx_prov_disc_info; struct tx_invite_req_info invitereq_info; - struct profile_info profileinfo[ P2P_MAX_PERSISTENT_GROUP_NUM ]; /* Store the profile information of persistent group */ + struct profile_info profileinfo[P2P_MAX_PERSISTENT_GROUP_NUM]; /* Store the profile information of persistent group */ struct tx_invite_resp_info inviteresp_info; struct tx_nego_req_info nego_req_info; struct group_id_info groupid_info; /* Store the group id information when doing the group negotiation handshake. */ @@ -243,17 +243,17 @@ struct wifidirect_info{ u8 support_rate[8]; u8 p2p_wildcard_ssid[P2P_WILDCARD_SSID_LEN]; u8 intent; /* should only include the intent value. */ - u8 p2p_peer_interface_addr[ ETH_ALEN ]; - u8 p2p_peer_device_addr[ ETH_ALEN ]; + u8 p2p_peer_interface_addr[ETH_ALEN]; + u8 p2p_peer_device_addr[ETH_ALEN]; u8 peer_intent; /* Included the intent value and tie breaker value. */ - u8 device_name[ WPS_MAX_DEVICE_NAME_LEN ]; /* Device name for displaying on searching device screen */ + u8 device_name[WPS_MAX_DEVICE_NAME_LEN]; /* Device name for displaying on searching device screen */ u8 device_name_len; u8 profileindex; /* Used to point to the index of profileinfo array */ u8 peer_operating_ch; u8 find_phase_state_exchange_cnt; u16 device_password_id_for_nego; /* The device password ID for group negotation */ u8 negotiation_dialog_token; - u8 nego_ssid[ WLAN_SSID_MAXLEN ]; /* SSID information for group negotitation */ + u8 nego_ssid[WLAN_SSID_MAXLEN]; /* SSID information for group negotitation */ u8 nego_ssidlen; u8 p2p_group_ssid[WLAN_SSID_MAXLEN]; u8 p2p_group_ssid_len; @@ -287,13 +287,13 @@ struct wifidirect_info{ u8 driver_interface; /* Indicate DRIVER_WEXT or DRIVER_CFG80211 */ }; -struct tdls_ss_record{ /* signal strength record */ +struct tdls_ss_record { /* signal strength record */ u8 macaddr[ETH_ALEN]; u8 rx_pwd_ba11; u8 is_tdls_sta; /* true: direct link sta, false: else */ }; -struct tdls_info{ +struct tdls_info { u8 ap_prohibited; u8 link_established; u8 sta_cnt; @@ -489,7 +489,7 @@ int event_thread(void *context); extern void rtw_free_network_queue(struct adapter *adapter, u8 isfreeall); extern int rtw_init_mlme_priv(struct adapter *adapter);/* (struct mlme_priv *pmlmepriv); */ -extern void rtw_free_mlme_priv (struct mlme_priv *pmlmepriv); +extern void rtw_free_mlme_priv(struct mlme_priv *pmlmepriv); extern sint rtw_select_and_join_from_scanned_queue(struct mlme_priv *pmlmepriv); @@ -526,7 +526,7 @@ static inline void set_fwstate(struct mlme_priv *pmlmepriv, sint state) { pmlmepriv->fw_state |= state; /* FOR HW integration */ - if (_FW_UNDER_SURVEY ==state) { + if (_FW_UNDER_SURVEY == state) { pmlmepriv->bScanInProcess = true; } } @@ -535,7 +535,7 @@ static inline void _clr_fwstate_(struct mlme_priv *pmlmepriv, sint state) { pmlmepriv->fw_state &= ~state; /* FOR HW integration */ - if (_FW_UNDER_SURVEY ==state) { + if (_FW_UNDER_SURVEY == state) { pmlmepriv->bScanInProcess = false; } } diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h index 73e8ec09b6e1..6c1ed6211c7e 100644 --- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h +++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h @@ -182,7 +182,7 @@ typedef enum _RT_CHANNEL_DOMAIN /* Add new channel plan above this line =============== */ RT_CHANNEL_DOMAIN_MAX, RT_CHANNEL_DOMAIN_REALTEK_DEFINE = 0x7F, -}RT_CHANNEL_DOMAIN, *PRT_CHANNEL_DOMAIN; +} RT_CHANNEL_DOMAIN, *PRT_CHANNEL_DOMAIN; typedef enum _RT_CHANNEL_DOMAIN_2G { @@ -195,7 +195,7 @@ typedef enum _RT_CHANNEL_DOMAIN_2G RT_CHANNEL_DOMAIN_2G_NULL = 0x06, /* Add new channel plan above this line =============== */ RT_CHANNEL_DOMAIN_2G_MAX, -}RT_CHANNEL_DOMAIN_2G, *PRT_CHANNEL_DOMAIN_2G; +} RT_CHANNEL_DOMAIN_2G, *PRT_CHANNEL_DOMAIN_2G; typedef enum _RT_CHANNEL_DOMAIN_5G { @@ -237,33 +237,33 @@ typedef enum _RT_CHANNEL_DOMAIN_5G RT_CHANNEL_DOMAIN_5G_JAPAN_NO_DFS = 0x21, RT_CHANNEL_DOMAIN_5G_FCC4_NO_DFS = 0x22, RT_CHANNEL_DOMAIN_5G_MAX, -}RT_CHANNEL_DOMAIN_5G, *PRT_CHANNEL_DOMAIN_5G; +} RT_CHANNEL_DOMAIN_5G, *PRT_CHANNEL_DOMAIN_5G; -#define rtw_is_channel_plan_valid(chplan) (chplan>2]; - }u; + } u; /* uint mem[MAX_RXSZ>>2]; */ @@ -388,8 +388,8 @@ enum RX_PACKET_TYPE { C2H_PACKET }; -extern union recv_frame *_rtw_alloc_recvframe (struct __queue *pfree_recv_queue); /* get a free recv_frame from pfree_recv_queue */ -extern union recv_frame *rtw_alloc_recvframe (struct __queue *pfree_recv_queue); /* get a free recv_frame from pfree_recv_queue */ +extern union recv_frame *_rtw_alloc_recvframe(struct __queue *pfree_recv_queue); /* get a free recv_frame from pfree_recv_queue */ +extern union recv_frame *rtw_alloc_recvframe(struct __queue *pfree_recv_queue); /* get a free recv_frame from pfree_recv_queue */ extern int rtw_free_recvframe(union recv_frame *precvframe, struct __queue *pfree_recv_queue); #define rtw_dequeue_recvframe(queue) rtw_alloc_recvframe(queue) @@ -401,7 +401,7 @@ u32 rtw_free_uc_swdec_pending_queue(struct adapter *adapter); sint rtw_enqueue_recvbuf_to_head(struct recv_buf *precvbuf, struct __queue *queue); sint rtw_enqueue_recvbuf(struct recv_buf *precvbuf, struct __queue *queue); -struct recv_buf *rtw_dequeue_recvbuf (struct __queue *queue); +struct recv_buf *rtw_dequeue_recvbuf(struct __queue *queue); void rtw_reordering_ctrl_timeout_handler(struct timer_list *t); @@ -444,7 +444,7 @@ static inline u8 *recvframe_pull(union recv_frame *precvframe, sint sz) return NULL; } - precvframe->u.hdr.len -=sz; + precvframe->u.hdr.len -= sz; return precvframe->u.hdr.rx_data; @@ -471,7 +471,7 @@ static inline u8 *recvframe_put(union recv_frame *precvframe, sint sz) return NULL; } - precvframe->u.hdr.len +=sz; + precvframe->u.hdr.len += sz; return precvframe->u.hdr.rx_tail; @@ -497,7 +497,7 @@ static inline u8 *recvframe_pull_tail(union recv_frame *precvframe, sint sz) return NULL; } - precvframe->u.hdr.len -=sz; + precvframe->u.hdr.len -= sz; return precvframe->u.hdr.rx_tail; diff --git a/drivers/staging/rtl8723bs/include/rtw_security.h b/drivers/staging/rtl8723bs/include/rtw_security.h index bea184452edd..aa60b6f867dd 100644 --- a/drivers/staging/rtl8723bs/include/rtw_security.h +++ b/drivers/staging/rtl8723bs/include/rtw_security.h @@ -208,7 +208,7 @@ struct sha256_state { }; #define GET_ENCRY_ALGO(psecuritypriv, psta, encry_algo, bmcst)\ -do{\ +do {\ switch (psecuritypriv->dot11AuthAlgrthm)\ {\ case dot11AuthAlgrthm_Open:\ @@ -220,18 +220,18 @@ do{\ if (bmcst)\ encry_algo = (u8)psecuritypriv->dot118021XGrpPrivacy;\ else\ - encry_algo =(u8) psta->dot118021XPrivacy;\ + encry_algo = (u8)psta->dot118021XPrivacy;\ break;\ case dot11AuthAlgrthm_WAPI:\ encry_algo = (u8)psecuritypriv->dot11PrivacyAlgrthm;\ break;\ - }\ -}while (0) + } \ +} while (0) #define _AES_IV_LEN_ 8 #define SET_ICE_IV_LEN(iv_len, icv_len, encrypt)\ -do{\ +do {\ switch (encrypt)\ {\ case _WEP40_:\ @@ -255,19 +255,19 @@ do{\ iv_len = 0;\ icv_len = 0;\ break;\ - }\ -}while (0) + } \ +} while (0) #define GET_TKIP_PN(iv, dot11txpn)\ -do{\ - dot11txpn._byte_.TSC0 =iv[2];\ - dot11txpn._byte_.TSC1 =iv[0];\ - dot11txpn._byte_.TSC2 =iv[4];\ - dot11txpn._byte_.TSC3 =iv[5];\ - dot11txpn._byte_.TSC4 =iv[6];\ - dot11txpn._byte_.TSC5 =iv[7];\ -}while (0) +do {\ + dot11txpn._byte_.TSC0 = iv[2];\ + dot11txpn._byte_.TSC1 = iv[0];\ + dot11txpn._byte_.TSC2 = iv[4];\ + dot11txpn._byte_.TSC3 = iv[5];\ + dot11txpn._byte_.TSC4 = iv[6];\ + dot11txpn._byte_.TSC5 = iv[7];\ +} while (0) #define ROL32(A, n) (((A) << (n)) | (((A)>>(32-(n))) & ((1UL << (n)) - 1))) diff --git a/drivers/staging/rtl8723bs/include/rtw_xmit.h b/drivers/staging/rtl8723bs/include/rtw_xmit.h index ea1396005a13..cd2be0056aa1 100644 --- a/drivers/staging/rtl8723bs/include/rtw_xmit.h +++ b/drivers/staging/rtl8723bs/include/rtw_xmit.h @@ -40,17 +40,17 @@ #define HW_QUEUE_ENTRY 8 #define WEP_IV(pattrib_iv, dot11txpn, keyidx)\ -do{\ +do {\ pattrib_iv[0] = dot11txpn._byte_.TSC0;\ pattrib_iv[1] = dot11txpn._byte_.TSC1;\ pattrib_iv[2] = dot11txpn._byte_.TSC2;\ pattrib_iv[3] = ((keyidx & 0x3)<<6);\ - dot11txpn.val = (dot11txpn.val == 0xffffff) ? 0: (dot11txpn.val+1);\ -}while (0) + dot11txpn.val = (dot11txpn.val == 0xffffff) ? 0 : (dot11txpn.val + 1);\ +} while (0) #define TKIP_IV(pattrib_iv, dot11txpn, keyidx)\ -do{\ +do {\ pattrib_iv[0] = dot11txpn._byte_.TSC1;\ pattrib_iv[1] = (dot11txpn._byte_.TSC1 | 0x20) & 0x7f;\ pattrib_iv[2] = dot11txpn._byte_.TSC0;\ @@ -59,11 +59,11 @@ do{\ pattrib_iv[5] = dot11txpn._byte_.TSC3;\ pattrib_iv[6] = dot11txpn._byte_.TSC4;\ pattrib_iv[7] = dot11txpn._byte_.TSC5;\ - dot11txpn.val = dot11txpn.val == 0xffffffffffffULL ? 0: (dot11txpn.val+1);\ -}while (0) + dot11txpn.val = dot11txpn.val == 0xffffffffffffULL ? 0 : (dot11txpn.val + 1);\ +} while (0) #define AES_IV(pattrib_iv, dot11txpn, keyidx)\ -do{\ +do {\ pattrib_iv[0] = dot11txpn._byte_.TSC0;\ pattrib_iv[1] = dot11txpn._byte_.TSC1;\ pattrib_iv[2] = 0;\ @@ -72,8 +72,8 @@ do{\ pattrib_iv[5] = dot11txpn._byte_.TSC3;\ pattrib_iv[6] = dot11txpn._byte_.TSC4;\ pattrib_iv[7] = dot11txpn._byte_.TSC5;\ - dot11txpn.val = dot11txpn.val == 0xffffffffffffULL ? 0: (dot11txpn.val+1);\ -}while (0) + dot11txpn.val = dot11txpn.val == 0xffffffffffffULL ? 0 : (dot11txpn.val + 1);\ +} while (0) #define HWXMIT_ENTRY 4 @@ -217,7 +217,7 @@ enum { XMITBUF_CMD = 2, }; -struct submit_ctx{ +struct submit_ctx { unsigned long submit_time; /* */ u32 timeout_ms; /* <0: not synchronous, 0: wait forever, >0: up to ms waiting */ int status; /* status for operation */ @@ -274,7 +274,7 @@ struct xmit_buf u8 pg_num; u8 agg_num; -#if defined(DBG_XMIT_BUF)|| defined(DBG_XMIT_BUF_EXT) +#if defined(DBG_XMIT_BUF) || defined(DBG_XMIT_BUF_EXT) u8 no; #endif @@ -350,7 +350,7 @@ struct hw_txqueue { sint ac_tag; }; -struct agg_pkt_info{ +struct agg_pkt_info { u16 offset; u16 pkt_len; }; @@ -484,7 +484,7 @@ void rtw_init_hwxmits(struct hw_xmit *phwxmit, sint entry); s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter); -void _rtw_free_xmit_priv (struct xmit_priv *pxmitpriv); +void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv); s32 rtw_alloc_hwxmits(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/include/sta_info.h b/drivers/staging/rtl8723bs/include/sta_info.h index 3acce5630f8e..c9aa3b5097a7 100644 --- a/drivers/staging/rtl8723bs/include/sta_info.h +++ b/drivers/staging/rtl8723bs/include/sta_info.h @@ -31,13 +31,13 @@ struct wlan_acl_pool { struct __queue acl_node_q; }; -typedef struct _RSSI_STA{ +typedef struct _RSSI_STA { s32 UndecoratedSmoothedPWDB; s32 UndecoratedSmoothedCCK; s32 UndecoratedSmoothedOFDM; u64 PacketMap; u8 ValidBit; -}RSSI_STA, *PRSSI_STA; +} RSSI_STA, *PRSSI_STA; struct stainfo_stats { @@ -304,7 +304,7 @@ struct sta_info { #define STA_RX_PKTS_DIFF_ARG(sta) \ sta->sta_stats.rx_mgnt_pkts - sta->sta_stats.last_rx_mgnt_pkts \ , sta->sta_stats.rx_ctrl_pkts - sta->sta_stats.last_rx_ctrl_pkts \ - , sta->sta_stats.rx_data_pkts -sta->sta_stats.last_rx_data_pkts + , sta->sta_stats.rx_data_pkts - sta->sta_stats.last_rx_data_pkts #define STA_PKTS_FMT "(m:%llu, c:%llu, d:%llu)" @@ -374,7 +374,7 @@ int rtw_stainfo_offset(struct sta_priv *stapriv, struct sta_info *sta); struct sta_info *rtw_get_stainfo_by_offset(struct sta_priv *stapriv, int offset); extern struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr); -extern u32 rtw_free_stainfo(struct adapter *padapter , struct sta_info *psta); +extern u32 rtw_free_stainfo(struct adapter *padapter, struct sta_info *psta); extern void rtw_free_all_stainfo(struct adapter *padapter); extern struct sta_info *rtw_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr); extern u32 rtw_init_bcmc_stainfo(struct adapter *padapter); diff --git a/drivers/staging/rtl8723bs/include/wifi.h b/drivers/staging/rtl8723bs/include/wifi.h index 2faf83704ff0..88a6e982ce01 100644 --- a/drivers/staging/rtl8723bs/include/wifi.h +++ b/drivers/staging/rtl8723bs/include/wifi.h @@ -707,7 +707,7 @@ struct HT_caps_element unsigned char ASEL_caps; } HT_cap_element; unsigned char HT_cap[26]; - }u; + } u; } __attribute__ ((packed)); struct HT_info_element @@ -1102,7 +1102,7 @@ enum P2P_PROTO_WK_ID P2P_PRE_TX_PROVDISC_PROCESS_WK = 2, P2P_PRE_TX_NEGOREQ_PROCESS_WK = 3, P2P_PRE_TX_INVITEREQ_PROCESS_WK = 4, - P2P_AP_P2P_CH_SWITCH_PROCESS_WK =5, + P2P_AP_P2P_CH_SWITCH_PROCESS_WK = 5, P2P_RO_CH_WK = 6, }; @@ -1126,8 +1126,8 @@ enum P2P_PROTO_WK_ID #define WFD_DEVINFO_PC_TDLS 0x0080 #define WFD_DEVINFO_HDCP_SUPPORT 0x0100 -#define IP_MCAST_MAC(mac) ((mac[0]== 0x01) && (mac[1]== 0x00) && (mac[2]== 0x5e)) -#define ICMPV6_MCAST_MAC(mac) ((mac[0]== 0x33) && (mac[1]== 0x33) && (mac[2]!= 0xff)) +#define IP_MCAST_MAC(mac) ((mac[0] == 0x01) && (mac[1] == 0x00) && (mac[2] == 0x5e)) +#define ICMPV6_MCAST_MAC(mac) ((mac[0] == 0x33) && (mac[1] == 0x33) && (mac[2] != 0xff)) /* Regulatroy Domain */ struct regd_pair_mapping { diff --git a/drivers/staging/rtl8723bs/include/xmit_osdep.h b/drivers/staging/rtl8723bs/include/xmit_osdep.h index a61412b54ec0..e9ff274f7474 100644 --- a/drivers/staging/rtl8723bs/include/xmit_osdep.h +++ b/drivers/staging/rtl8723bs/include/xmit_osdep.h @@ -35,8 +35,8 @@ void rtw_os_xmit_resource_free(struct adapter *padapter, struct xmit_buf *pxmitb extern uint rtw_remainder_len(struct pkt_file *pfile); extern void _rtw_open_pktfile(_pkt *pkt, struct pkt_file *pfile); -extern uint _rtw_pktfile_read (struct pkt_file *pfile, u8 *rmem, uint rlen); -extern sint rtw_endofpktfile (struct pkt_file *pfile); +extern uint _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen); +extern sint rtw_endofpktfile(struct pkt_file *pfile); extern void rtw_os_pkt_complete(struct adapter *padapter, _pkt *pkt); extern void rtw_os_xmit_complete(struct adapter *padapter, struct xmit_frame *pxframe); diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 322cabb97b99..1ba85a43f05a 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -254,7 +254,7 @@ struct cfg80211_bss *rtw_cfg80211_inform_bss(struct adapter *padapter, struct wl /* DBG_8192C("%s\n", __func__); */ - bssinf_len = pnetwork->network.IELength+sizeof (struct ieee80211_hdr_3addr); + bssinf_len = pnetwork->network.IELength + sizeof(struct ieee80211_hdr_3addr); if (bssinf_len > MAX_BSSINFO_LEN) { DBG_871X("%s IE Length too long > %d byte\n", __func__, MAX_BSSINFO_LEN); goto exit; @@ -263,7 +263,7 @@ struct cfg80211_bss *rtw_cfg80211_inform_bss(struct adapter *padapter, struct wl { u16 wapi_len = 0; - if (rtw_get_wapi_ie(pnetwork->network.IEs, pnetwork->network.IELength, NULL, &wapi_len)>0) + if (rtw_get_wapi_ie(pnetwork->network.IEs, pnetwork->network.IELength, NULL, &wapi_len) > 0) { if (wapi_len > 0) { @@ -286,7 +286,7 @@ struct cfg80211_bss *rtw_cfg80211_inform_bss(struct adapter *padapter, struct wl wpsie = rtw_get_wps_ie(pnetwork->network.IEs+_FIXED_IE_LENGTH_, pnetwork->network.IELength-_FIXED_IE_LENGTH_, NULL, &wpsielen); - if (wpsie && wpsielen>0) + if (wpsie && wpsielen > 0) psr = rtw_get_wps_attr_content(wpsie, wpsielen, WPS_ATTR_SELECTED_REGISTRAR, (u8 *)(&sr), NULL); if (sr != 0) @@ -353,7 +353,7 @@ struct cfg80211_bss *rtw_cfg80211_inform_bss(struct adapter *padapter, struct wl pbuf += sizeof(struct ieee80211_hdr_3addr); - len = sizeof (struct ieee80211_hdr_3addr); + len = sizeof(struct ieee80211_hdr_3addr); memcpy(pbuf, pnetwork->network.IEs, pnetwork->network.IELength); len += pnetwork->network.IELength; @@ -402,7 +402,7 @@ int rtw_cfg80211_check_bss(struct adapter *padapter) cfg80211_put_bss(padapter->rtw_wdev->wiphy, bss); - return (bss!= NULL); + return (bss != NULL); } void rtw_cfg80211_ibss_indicate_connect(struct adapter *padapter) @@ -424,7 +424,7 @@ void rtw_cfg80211_ibss_indicate_connect(struct adapter *padapter) struct wlan_bssid_ex *pnetwork = &(padapter->mlmeextpriv.mlmext_info.network); struct wlan_network *scanned = pmlmepriv->cur_network_scanned; - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ==true) + if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { memcpy(&cur_network->network, pnetwork, sizeof(struct wlan_bssid_ex)); @@ -579,7 +579,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa struct sta_info *psta = NULL, *pbcmc_sta = NULL; struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct security_priv* psecuritypriv =&(padapter->securitypriv); + struct security_priv* psecuritypriv = &(padapter->securitypriv); struct sta_priv *pstapriv = &padapter->stapriv; DBG_8192C("%s\n", __func__); @@ -633,7 +633,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa DBG_8192C("r871x_set_encryption, wep_key_idx =%d, len =%d\n", wep_key_idx, wep_key_len); - if ((wep_key_idx >= WEP_KEYS) || (wep_key_len<= 0)) + if ((wep_key_idx >= WEP_KEYS) || (wep_key_len <= 0)) { ret = -EINVAL; goto exit; @@ -673,7 +673,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa } - if (!psta && check_fwstate(pmlmepriv, WIFI_AP_STATE)) /* group key */ + if (!psta && check_fwstate(pmlmepriv, WIFI_AP_STATE)) /* group key */ { if (param->u.crypt.set_tx == 0) /* group key */ { @@ -681,7 +681,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa { DBG_8192C("%s, set group_key, WEP\n", __func__); - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); psecuritypriv->dot118021XGrpPrivacy = _WEP40_; if (param->u.crypt.key_len == 13) @@ -696,7 +696,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa psecuritypriv->dot118021XGrpPrivacy = _TKIP_; - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ @@ -712,7 +712,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa psecuritypriv->dot118021XGrpPrivacy = _AES_; - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); } else { @@ -729,7 +729,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa rtw_ap_set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx); - pbcmc_sta =rtw_get_bcmc_stainfo(padapter); + pbcmc_sta = rtw_get_bcmc_stainfo(padapter); if (pbcmc_sta) { pbcmc_sta->ieee8021x_blocked = false; @@ -748,7 +748,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa { if (param->u.crypt.set_tx == 1) /* pairwise key */ { - memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); if (strcmp(param->u.crypt.alg, "WEP") == 0) { @@ -799,7 +799,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa { if (strcmp(param->u.crypt.alg, "WEP") == 0) { - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); psecuritypriv->dot118021XGrpPrivacy = _WEP40_; if (param->u.crypt.key_len == 13) @@ -811,7 +811,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa { psecuritypriv->dot118021XGrpPrivacy = _TKIP_; - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ @@ -825,7 +825,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa { psecuritypriv->dot118021XGrpPrivacy = _AES_; - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); } else { @@ -840,7 +840,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa rtw_ap_set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx); - pbcmc_sta =rtw_get_bcmc_stainfo(padapter); + pbcmc_sta = rtw_get_bcmc_stainfo(padapter); if (pbcmc_sta) { pbcmc_sta->ieee8021x_blocked = false; @@ -940,7 +940,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param if (padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) /* 802_1x */ { - struct sta_info * psta,*pbcmc_sta; + struct sta_info * psta, *pbcmc_sta; struct sta_priv * pstapriv = &padapter->stapriv; /* DBG_8192C("%s, : dot11AuthAlgrthm == dot11AuthAlgrthm_8021X\n", __func__); */ @@ -959,7 +959,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param psta->ieee8021x_blocked = false; - if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled)|| + if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) || (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) { psta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm; @@ -970,7 +970,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param DBG_8192C("%s, : param->u.crypt.set_tx == 1\n", __func__); - memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); if (strcmp(param->u.crypt.alg, "TKIP") == 0)/* set mic key */ { @@ -978,7 +978,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8); memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8); - padapter->securitypriv.busetkipkey =false; + padapter->securitypriv.busetkipkey = false; /* _set_timer(&padapter->securitypriv.tkip_timer, 50); */ } @@ -991,21 +991,21 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param { if (strcmp(param->u.crypt.alg, "TKIP") == 0 || strcmp(param->u.crypt.alg, "CCMP") == 0) { - memcpy(padapter->securitypriv.dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); - memcpy(padapter->securitypriv.dot118021XGrptxmickey[param->u.crypt.idx].skey,&(param->u.crypt.key[16]), 8); - memcpy(padapter->securitypriv.dot118021XGrprxmickey[param->u.crypt.idx].skey,&(param->u.crypt.key[24]), 8); + memcpy(padapter->securitypriv.dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); + memcpy(padapter->securitypriv.dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8); + memcpy(padapter->securitypriv.dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8); padapter->securitypriv.binstallGrpkey = true; /* DEBUG_ERR((" param->u.crypt.key_len =%d\n", param->u.crypt.key_len)); */ DBG_871X(" ~~~~set sta key:groupkey\n"); padapter->securitypriv.dot118021XGrpKeyid = param->u.crypt.idx; - rtw_set_key(padapter,&padapter->securitypriv, param->u.crypt.idx, 1, true); + rtw_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx, 1, true); } else if (strcmp(param->u.crypt.alg, "BIP") == 0) { /* DBG_871X("BIP key_len =%d , index =%d @@@@@@@@@@@@@@@@@@\n", param->u.crypt.key_len, param->u.crypt.idx); */ /* save the IGTK key, length 16 bytes */ - memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); /*DBG_871X("IGTK key below:\n"); for (no = 0;no<16;no++) printk(" %02x ", padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey[no]); @@ -1017,7 +1017,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param } } - pbcmc_sta =rtw_get_bcmc_stainfo(padapter); + pbcmc_sta = rtw_get_bcmc_stainfo(padapter); if (pbcmc_sta == NULL) { /* DEBUG_ERR(("Set OID_802_11_ADD_KEY: bcmc stainfo is null\n")); */ @@ -1028,7 +1028,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param if (strcmp(param->u.crypt.alg, "none") != 0) pbcmc_sta->ieee8021x_blocked = false; - if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled)|| + if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) || (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) { pbcmc_sta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm; @@ -1270,8 +1270,8 @@ static int cfg80211_rtw_get_station(struct wiphy *wiphy, /* for Ad-Hoc/AP mode */ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) - ||check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) - ||check_fwstate(pmlmepriv, WIFI_AP_STATE)) + || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) + || check_fwstate(pmlmepriv, WIFI_AP_STATE)) && check_fwstate(pmlmepriv, _FW_LINKED) ) { @@ -1346,7 +1346,7 @@ static int cfg80211_rtw_change_iface(struct wiphy *wiphy, rtw_wdev->iftype = type; - if (rtw_set_802_11_infrastructure_mode(padapter, networkType) ==false) + if (rtw_set_802_11_infrastructure_mode(padapter, networkType) == false) { rtw_wdev->iftype = old_type; ret = -EPERM; @@ -1464,7 +1464,7 @@ static int rtw_cfg80211_set_probe_req_wpsp2pie(struct adapter *padapter, char *b DBG_8192C("%s, ielen =%d\n", __func__, len); #endif - if (len>0) + if (len > 0) { if ((wps_ie = rtw_get_wps_ie(buf, len, NULL, &wps_ielen))) { @@ -1503,8 +1503,8 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy int ret = 0; struct ndis_802_11_ssid *ssid = NULL; struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT]; - u8 survey_times =3; - u8 survey_times_for_one_ch =6; + u8 survey_times = 3; + u8 survey_times_for_one_ch = 6; struct cfg80211_ssid *ssids = request->ssids; int j = 0; bool need_indicate_scan_done = false; @@ -1556,7 +1556,7 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy goto check_need_indicate_scan_done; } - if (request->ie && request->ie_len>0) + if (request->ie && request->ie_len > 0) { rtw_cfg80211_set_probe_req_wpsp2pie(padapter, (u8 *)request->ie, request->ie_len); } @@ -1610,7 +1610,7 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy /* parsing channels, n_channels */ memset(ch, 0, sizeof(struct rtw_ieee80211_channel)*RTW_CHANNEL_SCAN_AMOUNT); - for (i = 0;in_channels && in_channels && i < RTW_CHANNEL_SCAN_AMOUNT; i++) { #ifdef DEBUG_CFG80211 DBG_871X(FUNC_ADPT_FMT CHAN_FMT"\n", FUNC_ADPT_ARG(padapter), CHAN_ARG(request->channels[i])); #endif @@ -1620,12 +1620,12 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy spin_lock_bh(&pmlmepriv->lock); if (request->n_channels == 1) { - for (i = 1;in_channels <= 4) { - for (j =request->n_channels-1;j>= 0;j--) - for (i = 0;in_channels - 1; j >= 0; j--) + for (i = 0; i < survey_times; i++) { memcpy(&ch[j*survey_times+i], &ch[j], sizeof(struct rtw_ieee80211_channel)); } @@ -1699,7 +1699,7 @@ static int rtw_cfg80211_set_auth_type(struct security_priv *psecuritypriv, psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; - if (psecuritypriv->ndisauthtype>Ndis802_11AuthModeWPA) + if (psecuritypriv->ndisauthtype > Ndis802_11AuthModeWPA) psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; break; @@ -1767,7 +1767,7 @@ static int rtw_cfg80211_set_cipher(struct security_priv *psecuritypriv, u32 ciph psecuritypriv->ndisencryptstatus = ndisencryptstatus; /* if (psecuritypriv->dot11PrivacyAlgrthm >= _AES_) */ - /* psecuritypriv->ndisauthtype = Ndis802_11AuthModeWPA2PSK; */ + /* psecuritypriv->ndisauthtype = Ndis802_11AuthModeWPA2PSK; */ } return 0; @@ -1799,7 +1799,7 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel int wpa_ielen = 0; int wpa2_ielen = 0; u8 *pwpa, *pwpa2; - u8 null_addr[]= {0, 0, 0, 0, 0, 0}; + u8 null_addr[] = {0, 0, 0, 0, 0, 0}; if (pie == NULL || !ielen) { /* Treat this as normal case, but need to clear WIFI_UNDER_WPS */ @@ -1818,13 +1818,13 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel goto exit; } - memcpy(buf, pie , ielen); + memcpy(buf, pie, ielen); /* dump */ { int i; DBG_8192C("set wpa_ie(length:%zu):\n", ielen); - for (i = 0;i0) + if (pwpa && wpa_ielen > 0) { if (rtw_parse_wpa_ie(pwpa, wpa_ielen+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; - padapter->securitypriv.ndisauthtype =Ndis802_11AuthModeWPAPSK; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen+2); DBG_8192C("got wpa_ie, wpa_ielen:%u\n", wpa_ielen); @@ -1848,12 +1848,12 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel } pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen); - if (pwpa2 && wpa2_ielen>0) + if (pwpa2 && wpa2_ielen > 0) { if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; - padapter->securitypriv.ndisauthtype =Ndis802_11AuthModeWPA2PSK; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; memcpy(padapter->securitypriv.supplicant_ie, &pwpa2[0], wpa2_ielen+2); DBG_8192C("got wpa2_ie, wpa2_ielen:%u\n", wpa2_ielen); @@ -1873,7 +1873,7 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel { case WPA_CIPHER_NONE: padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; - padapter->securitypriv.ndisencryptstatus =Ndis802_11EncryptionDisabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; break; case WPA_CIPHER_WEP40: padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_; @@ -1897,7 +1897,7 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel { case WPA_CIPHER_NONE: padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; - padapter->securitypriv.ndisencryptstatus =Ndis802_11EncryptionDisabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; break; case WPA_CIPHER_WEP40: padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_; @@ -1924,7 +1924,7 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel wps_ie = rtw_get_wps_ie(buf, ielen, NULL, &wps_ielen); if (wps_ie && wps_ielen > 0) { DBG_8192C("got wps_ie, wps_ielen:%u\n", wps_ielen); - padapter->securitypriv.wps_ie_len = wps_ielensecuritypriv.wps_ie_len = wps_ielen < MAX_WPS_IE_LEN ? wps_ielen : MAX_WPS_IE_LEN; memcpy(padapter->securitypriv.wps_ie, wps_ie, padapter->securitypriv.wps_ie_len); set_fwstate(&padapter->mlmepriv, WIFI_UNDER_WPS); } else { @@ -2027,7 +2027,7 @@ static int cfg80211_rtw_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) rtw_wdev->iftype = NL80211_IFTYPE_STATION; - if (rtw_set_802_11_infrastructure_mode(padapter, Ndis802_11Infrastructure) ==false) + if (rtw_set_802_11_infrastructure_mode(padapter, Ndis802_11Infrastructure) == false) { rtw_wdev->iftype = old_type; ret = -EPERM; @@ -2185,7 +2185,7 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, if (rtw_set_802_11_add_wep(padapter, pwep) == (u8)_FAIL) { - ret = -EOPNOTSUPP ; + ret = -EOPNOTSUPP; } kfree(pwep); @@ -2301,7 +2301,7 @@ static int cfg80211_rtw_set_pmksa(struct wiphy *wiphy, u8 index, blInserted = false; struct adapter *padapter = (struct adapter *)rtw_netdev_priv(ndev); struct security_priv *psecuritypriv = &padapter->securitypriv; - u8 strZeroMacAddress[ ETH_ALEN ] = { 0x00 }; + u8 strZeroMacAddress[ETH_ALEN] = { 0x00 }; DBG_871X(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev)); @@ -2313,7 +2313,7 @@ static int cfg80211_rtw_set_pmksa(struct wiphy *wiphy, blInserted = false; /* overwrite PMKID */ - for (index = 0 ; indexPMKIDList[index].Bssid, (u8 *)pmksa->bssid, ETH_ALEN)) { /* BSSID is matched, the same AP => rewrite with new PMKID. */ @@ -2337,7 +2337,7 @@ static int cfg80211_rtw_set_pmksa(struct wiphy *wiphy, memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].PMKID, (u8 *)pmksa->pmkid, WLAN_PMKID_LEN); psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].bUsed = true; - psecuritypriv->PMKIDIndex++ ; + psecuritypriv->PMKIDIndex++; if (psecuritypriv->PMKIDIndex == 16) { psecuritypriv->PMKIDIndex = 0; @@ -2357,7 +2357,7 @@ static int cfg80211_rtw_del_pmksa(struct wiphy *wiphy, DBG_871X(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev)); - for (index = 0 ; indexPMKIDList[index].Bssid, (u8 *)pmksa->bssid, ETH_ALEN)) { /* BSSID is matched, the same AP => Remove this PMKID information and reset it. */ @@ -2387,7 +2387,7 @@ static int cfg80211_rtw_flush_pmksa(struct wiphy *wiphy, DBG_871X(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev)); - memset(&psecuritypriv->PMKIDList[ 0 ], 0x00, sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE); + memset(&psecuritypriv->PMKIDList[0], 0x00, sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE); psecuritypriv->PMKIDIndex = 0; return 0; @@ -2575,7 +2575,7 @@ static const struct net_device_ops rtw_cfg80211_monitor_if_ops = { .ndo_start_xmit = rtw_cfg80211_monitor_if_xmit_entry, }; -static int rtw_cfg80211_add_monitor_if (struct adapter *padapter, char *name, struct net_device **ndev) +static int rtw_cfg80211_add_monitor_if(struct adapter *padapter, char *name, struct net_device **ndev) { int ret = 0; struct net_device* mon_ndev = NULL; @@ -2734,7 +2734,7 @@ static int rtw_add_beacon(struct adapter *adapter, const u8 *head, size_t head_l if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true) return -EINVAL; - if (head_len<24) + if (head_len < 24) return -EINVAL; pbuf = rtw_zmalloc(head_len+tail_len); @@ -3276,7 +3276,7 @@ static void rtw_cfg80211_init_ht_capab(struct ieee80211_sta_ht_cap *ht_cap, enum ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS7); } - else if ((rf_type == RF_1T2R) || (rf_type ==RF_2T2R)) + else if ((rf_type == RF_1T2R) || (rf_type == RF_2T2R)) { ht_cap->mcs.rx_mask[0] = 0xFF; ht_cap->mcs.rx_mask[1] = 0xFF; diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c index db6528a01229..d9a81d0a2d52 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_linux.c @@ -59,7 +59,7 @@ void rtw_indicate_wx_assoc_event(struct adapter *padapter) wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ==true) + if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) memcpy(wrqu.ap_addr.sa_data, pnetwork->MacAddress, ETH_ALEN); else memcpy(wrqu.ap_addr.sa_data, pmlmepriv->cur_network.network.MacAddress, ETH_ALEN); @@ -86,11 +86,11 @@ static char *translate_scan(struct adapter *padapter, u32 ht_ielen = 0; char *custom = NULL; char *p; - u16 max_rate = 0, rate, ht_cap =false, vht_cap = false; + u16 max_rate = 0, rate, ht_cap = false, vht_cap = false; u32 i = 0; u8 bw_40MHz = 0, short_GI = 0; u16 mcs_rate = 0, vht_data_rate = 0; - u8 ie_offset = (pnetwork->network.Reserved[0] == 2? 0:12); + u8 ie_offset = (pnetwork->network.Reserved[0] == 2 ? 0 : 12); struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); u8 ss, sq; @@ -113,11 +113,11 @@ static char *translate_scan(struct adapter *padapter, } else { p = rtw_get_ie(&pnetwork->network.IEs[12], _HT_CAPABILITY_IE_, &ht_ielen, pnetwork->network.IELength-12); } - if (p && ht_ielen>0) { + if (p && ht_ielen > 0) { struct rtw_ieee80211_ht_cap *pht_capie; ht_cap = true; pht_capie = (struct rtw_ieee80211_ht_cap *)(p+2); - memcpy(&mcs_rate , pht_capie->supp_mcs_set, 2); + memcpy(&mcs_rate, pht_capie->supp_mcs_set, 2); bw_40MHz = (le16_to_cpu(pht_capie->cap_info) & IEEE80211_HT_CAP_SUP_WIDTH) ? 1 : 0; short_GI = (le16_to_cpu(pht_capie->cap_info) & (IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40)) ? 1 : 0; } @@ -163,7 +163,7 @@ static char *translate_scan(struct adapter *padapter, cap = le16_to_cpu(le_tmp); } - if (cap & (WLAN_CAPABILITY_IBSS |WLAN_CAPABILITY_BSS)) { + if (cap & (WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_BSS)) { if (cap & WLAN_CAPABILITY_BSS) iwe.u.mode = IW_MODE_MASTER; else @@ -172,7 +172,7 @@ static char *translate_scan(struct adapter *padapter, start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN); } - if (pnetwork->network.Configuration.DSConfig<1 /*|| pnetwork->network.Configuration.DSConfig>14*/) + if (pnetwork->network.Configuration.DSConfig < 1 /*|| pnetwork->network.Configuration.DSConfig > 14*/) pnetwork->network.Configuration.DSConfig = 1; /* Add frequency/channel */ @@ -198,7 +198,7 @@ static char *translate_scan(struct adapter *padapter, return start; p = custom; p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); - while (pnetwork->network.SupportedRates[i]!= 0) { + while (pnetwork->network.SupportedRates[i] != 0) { rate = pnetwork->network.SupportedRates[i]&0x7F; if (rate > max_rate) max_rate = rate; @@ -239,7 +239,7 @@ static char *translate_scan(struct adapter *padapter, if (!buf) return start; if (wpa_len > 0) { - p =buf; + p = buf; p += sprintf(p, "wpa_ie ="); for (i = 0; i < wpa_len; i++) p += sprintf(p, "%02x", wpa_ie[i]); @@ -258,7 +258,7 @@ static char *translate_scan(struct adapter *padapter, start = iwe_stream_add_point(info, start, stop, &iwe, buf); memset(&iwe, 0, sizeof(iwe)); - iwe.cmd =IWEVGENIE; + iwe.cmd = IWEVGENIE; iwe.u.data.length = wpa_len; start = iwe_stream_add_point(info, start, stop, &iwe, wpa_ie); } @@ -274,7 +274,7 @@ static char *translate_scan(struct adapter *padapter, start = iwe_stream_add_point(info, start, stop, &iwe, buf); memset(&iwe, 0, sizeof(iwe)); - iwe.cmd =IWEVGENIE; + iwe.cmd = IWEVGENIE; iwe.u.data.length = rsn_len; start = iwe_stream_add_point(info, start, stop, &iwe, rsn_ie); } @@ -298,13 +298,13 @@ static char *translate_scan(struct adapter *padapter, } while (cnt < total_ielen) { - if (rtw_is_wps_ie(&ie_ptr[cnt], &wps_ielen) && (wps_ielen>2)) { + if (rtw_is_wps_ie(&ie_ptr[cnt], &wps_ielen) && (wps_ielen > 2)) { wpsie_ptr = &ie_ptr[cnt]; - iwe.cmd =IWEVGENIE; + iwe.cmd = IWEVGENIE; iwe.u.data.length = (u16)wps_ielen; start = iwe_stream_add_point(info, start, stop, &iwe, wpsie_ptr); } - cnt+=ie_ptr[cnt+1]+2; /* goto next */ + cnt += ie_ptr[cnt + 1] + 2; /* goto next */ } } @@ -352,8 +352,8 @@ static char *translate_scan(struct adapter *padapter, #if defined(CONFIG_SIGNAL_DISPLAY_DBM) && defined(CONFIG_BACKGROUND_NOISE_MONITOR) { s16 tmp_noise = 0; - rtw_hal_get_odm_var(padapter, HAL_ODM_NOISE_MONITOR,&(pnetwork->network.Configuration.DSConfig), &(tmp_noise)); - iwe.u.qual.noise = tmp_noise ; + rtw_hal_get_odm_var(padapter, HAL_ODM_NOISE_MONITOR, &(pnetwork->network.Configuration.DSConfig), &(tmp_noise)); + iwe.u.qual.noise = tmp_noise; } #else iwe.u.qual.noise = 0; /* noise level */ @@ -500,7 +500,7 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, DBG_871X("wep, set_tx = 1\n"); if (rtw_set_802_11_add_wep(padapter, pwep) == (u8)_FAIL) - ret = -EOPNOTSUPP ; + ret = -EOPNOTSUPP; } else { DBG_871X("wep, set_tx = 0\n"); @@ -508,12 +508,12 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, /* psecuritypriv->dot11PrivacyKeyIndex =keyid", but can rtw_set_key to fw/cam */ if (wep_key_idx >= WEP_KEYS) { - ret = -EOPNOTSUPP ; + ret = -EOPNOTSUPP; goto exit; } memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), pwep->KeyMaterial, pwep->KeyLength); - psecuritypriv->dot11DefKeylen[wep_key_idx]=pwep->KeyLength; + psecuritypriv->dot11DefKeylen[wep_key_idx] = pwep->KeyLength; rtw_set_key(padapter, psecuritypriv, wep_key_idx, 0, true); } @@ -533,20 +533,20 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, if (strcmp(param->u.crypt.alg, "none") != 0) psta->ieee8021x_blocked = false; - if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled)|| + if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) || (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) { psta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm; } if (param->u.crypt.set_tx == 1) { /* pairwise key */ - memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */ /* DEBUG_ERR(("\nset key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len)); */ memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8); memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8); - padapter->securitypriv.busetkipkey =false; + padapter->securitypriv.busetkipkey = false; /* _set_timer(&padapter->securitypriv.tkip_timer, 50); */ } @@ -556,11 +556,11 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, rtw_setstakey_cmd(padapter, psta, true, true); } else { /* group key */ if (strcmp(param->u.crypt.alg, "TKIP") == 0 || strcmp(param->u.crypt.alg, "CCMP") == 0) { - memcpy(padapter->securitypriv.dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(padapter->securitypriv.dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); /* only TKIP group key need to install this */ if (param->u.crypt.key_len > 16) { - memcpy(padapter->securitypriv.dot118021XGrptxmickey[param->u.crypt.idx].skey,&(param->u.crypt.key[16]), 8); - memcpy(padapter->securitypriv.dot118021XGrprxmickey[param->u.crypt.idx].skey,&(param->u.crypt.key[24]), 8); + memcpy(padapter->securitypriv.dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8); + memcpy(padapter->securitypriv.dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8); } padapter->securitypriv.binstallGrpkey = true; /* DEBUG_ERR((" param->u.crypt.key_len =%d\n", param->u.crypt.key_len)); */ @@ -568,11 +568,11 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, padapter->securitypriv.dot118021XGrpKeyid = param->u.crypt.idx; - rtw_set_key(padapter,&padapter->securitypriv, param->u.crypt.idx, 1, true); + rtw_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx, 1, true); } else if (strcmp(param->u.crypt.alg, "BIP") == 0) { /* printk("BIP key_len =%d , index =%d @@@@@@@@@@@@@@@@@@\n", param->u.crypt.key_len, param->u.crypt.idx); */ /* save the IGTK key, length 16 bytes */ - memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); /*printk("IGTK key below:\n"); for (no = 0;no<16;no++) printk(" %02x ", padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey[no]); @@ -584,7 +584,7 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, } } - pbcmc_sta =rtw_get_bcmc_stainfo(padapter); + pbcmc_sta = rtw_get_bcmc_stainfo(padapter); if (pbcmc_sta == NULL) { /* DEBUG_ERR(("Set OID_802_11_ADD_KEY: bcmc stainfo is null\n")); */ } else { @@ -592,7 +592,7 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, if (strcmp(param->u.crypt.alg, "none") != 0) pbcmc_sta->ieee8021x_blocked = false; - if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled)|| + if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) || (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) { pbcmc_sta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm; } @@ -613,7 +613,7 @@ static int rtw_set_wpa_ie(struct adapter *padapter, char *pie, unsigned short ie u8 *buf = NULL; int group_cipher = 0, pairwise_cipher = 0; int ret = 0; - u8 null_addr[]= {0, 0, 0, 0, 0, 0}; + u8 null_addr[] = {0, 0, 0, 0, 0, 0}; if ((ielen > MAX_WPA_IE_LEN) || (pie == NULL)) { _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS); @@ -630,13 +630,13 @@ static int rtw_set_wpa_ie(struct adapter *padapter, char *pie, unsigned short ie goto exit; } - memcpy(buf, pie , ielen); + memcpy(buf, pie, ielen); /* dump */ { int i; DBG_871X("\n wpa_ie(length:%d):\n", ielen); - for (i = 0;isecuritypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; - padapter->securitypriv.ndisauthtype =Ndis802_11AuthModeWPAPSK; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; memcpy(padapter->securitypriv.supplicant_ie, &buf[0], ielen); } if (rtw_parse_wpa2_ie(buf, ielen, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; - padapter->securitypriv.ndisauthtype =Ndis802_11AuthModeWPA2PSK; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; memcpy(padapter->securitypriv.supplicant_ie, &buf[0], ielen); } @@ -666,7 +666,7 @@ static int rtw_set_wpa_ie(struct adapter *padapter, char *pie, unsigned short ie switch (group_cipher) { case WPA_CIPHER_NONE: padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; - padapter->securitypriv.ndisencryptstatus =Ndis802_11EncryptionDisabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; break; case WPA_CIPHER_WEP40: padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_; @@ -689,7 +689,7 @@ static int rtw_set_wpa_ie(struct adapter *padapter, char *pie, unsigned short ie switch (pairwise_cipher) { case WPA_CIPHER_NONE: padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; - padapter->securitypriv.ndisencryptstatus =Ndis802_11EncryptionDisabled; + padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; break; case WPA_CIPHER_WEP40: padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_; @@ -712,7 +712,7 @@ static int rtw_set_wpa_ie(struct adapter *padapter, char *pie, unsigned short ie _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS); {/* set wps_ie */ u16 cnt = 0; - u8 eid, wps_oui[4]={0x0, 0x50, 0xf2, 0x04}; + u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; while (cnt < ielen) { eid = buf[cnt]; @@ -762,7 +762,7 @@ static int rtw_wx_get_name(struct net_device *dev, struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); u32 ht_ielen = 0; char *p; - u8 ht_cap =false, vht_cap =false; + u8 ht_cap = false, vht_cap = false; struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; NDIS_802_11_RATES_EX* prates = NULL; @@ -772,7 +772,7 @@ static int rtw_wx_get_name(struct net_device *dev, if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE) == true) { /* parsing HT_CAP_IE */ p = rtw_get_ie(&pcur_bss->IEs[12], _HT_CAPABILITY_IE_, &ht_ielen, pcur_bss->IELength-12); - if (p && ht_ielen>0) + if (p && ht_ielen > 0) ht_cap = true; prates = &pcur_bss->SupportedRates; @@ -846,7 +846,7 @@ static int rtw_wx_set_mode(struct net_device *dev, struct iw_request_info *a, union iwreq_data *wrqu, char *b) { struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); - enum NDIS_802_11_NETWORK_INFRASTRUCTURE networkType ; + enum NDIS_802_11_NETWORK_INFRASTRUCTURE networkType; int ret = 0; if (_FAIL == rtw_pwr_wakeup(padapter)) { @@ -878,7 +878,7 @@ static int rtw_wx_set_mode(struct net_device *dev, struct iw_request_info *a, DBG_871X("set_mode = IW_MODE_INFRA\n"); break; - default : + default: ret = -EINVAL; RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("\n Mode: %s is not supported \n", iw_operation_mode[wrqu->mode])); goto exit; @@ -895,7 +895,7 @@ static int rtw_wx_set_mode(struct net_device *dev, struct iw_request_info *a, } */ - if (rtw_set_802_11_infrastructure_mode(padapter, networkType) ==false) { + if (rtw_set_802_11_infrastructure_mode(padapter, networkType) == false) { ret = -EPERM; goto exit; @@ -939,8 +939,8 @@ static int rtw_wx_set_pmkid(struct net_device *dev, int intReturn = false; struct security_priv *psecuritypriv = &padapter->securitypriv; struct iw_pmksa* pPMK = (struct iw_pmksa *)extra; - u8 strZeroMacAddress[ ETH_ALEN ] = { 0x00 }; - u8 strIssueBssid[ ETH_ALEN ] = { 0x00 }; + u8 strZeroMacAddress[ETH_ALEN] = { 0x00 }; + u8 strIssueBssid[ETH_ALEN] = { 0x00 }; /* There are the BSSID information in the bssid.sa_data array. @@ -960,13 +960,13 @@ static int rtw_wx_set_pmkid(struct net_device *dev, blInserted = false; /* overwrite PMKID */ - for (j = 0 ; jPMKIDList[j].Bssid, strIssueBssid, ETH_ALEN)) { /* BSSID is matched, the same AP => rewrite with new PMKID. */ DBG_871X("[rtw_wx_set_pmkid] BSSID exists in the PMKList.\n"); memcpy(psecuritypriv->PMKIDList[j].PMKID, pPMK->pmkid, IW_PMKID_LEN); - psecuritypriv->PMKIDList[ j ].bUsed = true; + psecuritypriv->PMKIDList[j].bUsed = true; psecuritypriv->PMKIDIndex = j+1; blInserted = true; break; @@ -981,25 +981,25 @@ static int rtw_wx_set_pmkid(struct net_device *dev, memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].Bssid, strIssueBssid, ETH_ALEN); memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].PMKID, pPMK->pmkid, IW_PMKID_LEN); - psecuritypriv->PMKIDList[ psecuritypriv->PMKIDIndex ].bUsed = true; - psecuritypriv->PMKIDIndex++ ; + psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].bUsed = true; + psecuritypriv->PMKIDIndex++; if (psecuritypriv->PMKIDIndex == 16) psecuritypriv->PMKIDIndex = 0; } } else if (pPMK->cmd == IW_PMKSA_REMOVE) { DBG_871X("[rtw_wx_set_pmkid] IW_PMKSA_REMOVE!\n"); intReturn = true; - for (j = 0 ; jPMKIDList[j].Bssid, strIssueBssid, ETH_ALEN)) { /* BSSID is matched, the same AP => Remove this PMKID information and reset it. */ eth_zero_addr(psecuritypriv->PMKIDList[j].Bssid); - psecuritypriv->PMKIDList[ j ].bUsed = false; + psecuritypriv->PMKIDList[j].bUsed = false; break; } } } else if (pPMK->cmd == IW_PMKSA_FLUSH) { DBG_871X("[rtw_wx_set_pmkid] IW_PMKSA_FLUSH!\n"); - memset(&psecuritypriv->PMKIDList[ 0 ], 0x00, sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE); + memset(&psecuritypriv->PMKIDList[0], 0x00, sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE); psecuritypriv->PMKIDIndex = 0; intReturn = true; } @@ -1106,11 +1106,11 @@ static int rtw_wx_get_range(struct net_device *dev, #define IW_SCAN_CAPA_TIME 0x40 */ - range->enc_capa = IW_ENC_CAPA_WPA|IW_ENC_CAPA_WPA2| - IW_ENC_CAPA_CIPHER_TKIP|IW_ENC_CAPA_CIPHER_CCMP; + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; - range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE |IW_SCAN_CAPA_BSSID| - IW_SCAN_CAPA_CHANNEL|IW_SCAN_CAPA_MODE|IW_SCAN_CAPA_RATE; + range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE | IW_SCAN_CAPA_BSSID | + IW_SCAN_CAPA_CHANNEL | IW_SCAN_CAPA_MODE | IW_SCAN_CAPA_RATE; return 0; } @@ -1287,7 +1287,7 @@ static int rtw_wx_set_scan(struct net_device *dev, struct iw_request_info *a, goto exit; } - if (!padapter->hw_init_completed ) { + if (!padapter->hw_init_completed) { ret = -1; goto exit; } @@ -1330,7 +1330,7 @@ static int rtw_wx_set_scan(struct net_device *dev, struct iw_request_info *a, } else if (wrqu->data.length >= WEXT_CSCAN_HEADER_SIZE && !memcmp(extra, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE)) { - int len = wrqu->data.length -WEXT_CSCAN_HEADER_SIZE; + int len = wrqu->data.length - WEXT_CSCAN_HEADER_SIZE; char *pos = extra+WEXT_CSCAN_HEADER_SIZE; char section; char sec_len; @@ -1339,7 +1339,7 @@ static int rtw_wx_set_scan(struct net_device *dev, struct iw_request_info *a, /* DBG_871X("%s COMBO_SCAN header is recognized\n", __func__); */ while (len >= 1) { - section = *(pos++); len-= 1; + section = *(pos++); len -= 1; switch (section) { case WEXT_CSCAN_SSID_SECTION: @@ -1349,9 +1349,9 @@ static int rtw_wx_set_scan(struct net_device *dev, struct iw_request_info *a, break; } - sec_len = *(pos++); len-= 1; + sec_len = *(pos++); len -= 1; - if (sec_len>0 && sec_len<=len) { + if (sec_len > 0 && sec_len <= len) { ssid[ssid_index].SsidLength = sec_len; memcpy(ssid[ssid_index].Ssid, pos, ssid[ssid_index].SsidLength); /* DBG_871X("%s COMBO_SCAN with specific ssid:%s, %d\n", __func__ */ @@ -1359,29 +1359,29 @@ static int rtw_wx_set_scan(struct net_device *dev, struct iw_request_info *a, ssid_index++; } - pos+=sec_len; len-=sec_len; + pos += sec_len; len -= sec_len; break; case WEXT_CSCAN_CHANNEL_SECTION: /* DBG_871X("WEXT_CSCAN_CHANNEL_SECTION\n"); */ - pos+= 1; len-= 1; + pos += 1; len -= 1; break; case WEXT_CSCAN_ACTV_DWELL_SECTION: /* DBG_871X("WEXT_CSCAN_ACTV_DWELL_SECTION\n"); */ - pos+=2; len-=2; + pos += 2; len -= 2; break; case WEXT_CSCAN_PASV_DWELL_SECTION: /* DBG_871X("WEXT_CSCAN_PASV_DWELL_SECTION\n"); */ - pos+=2; len-=2; + pos += 2; len -= 2; break; case WEXT_CSCAN_HOME_DWELL_SECTION: /* DBG_871X("WEXT_CSCAN_HOME_DWELL_SECTION\n"); */ - pos+=2; len-=2; + pos += 2; len -= 2; break; case WEXT_CSCAN_TYPE_SECTION: /* DBG_871X("WEXT_CSCAN_TYPE_SECTION\n"); */ - pos+= 1; len-= 1; + pos += 1; len -= 1; break; default: /* DBG_871X("Unknown CSCAN section %c\n", section); */ @@ -1463,7 +1463,7 @@ static int rtw_wx_get_scan(struct net_device *dev, struct iw_request_info *a, && rtw_mlme_band_check(padapter, pnetwork->network.Configuration.DSConfig) == true && true == rtw_validate_ssid(&(pnetwork->network.Ssid))) { - ev =translate_scan(padapter, a, pnetwork, ev, stop); + ev = translate_scan(padapter, a, pnetwork, ev, stop); } plist = get_next(plist); @@ -1481,7 +1481,7 @@ exit: DBG_871X("DBG_IOCTL %s:%d return %d\n", __func__, __LINE__, ret); #endif - return ret ; + return ret; } @@ -1570,7 +1570,7 @@ static int rtw_wx_set_essid(struct net_device *dev, pnetwork->network.Ssid.Ssid)); if ((!memcmp(dst_ssid, src_ssid, ndis_ssid.SsidLength)) && - (pnetwork->network.Ssid.SsidLength ==ndis_ssid.SsidLength)) { + (pnetwork->network.Ssid.SsidLength == ndis_ssid.SsidLength)) { RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("rtw_wx_set_essid: find match, set infra mode\n")); @@ -1651,7 +1651,7 @@ static int rtw_wx_set_rate(struct net_device *dev, u32 target_rate = wrqu->bitrate.value; u32 fixed = wrqu->bitrate.fixed; u32 ratevalue = 0; - u8 mpdatarate[NumRates]={11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0xff}; + u8 mpdatarate[NumRates] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0xff}; RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, (" rtw_wx_set_rate\n")); RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("target_rate = %d, fixed = %d\n", target_rate, fixed)); @@ -1706,8 +1706,8 @@ static int rtw_wx_set_rate(struct net_device *dev, set_rate: - for (i = 0; isecuritypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */ authmode = Ndis802_11AuthModeOpen; - padapter->securitypriv.ndisauthtype =authmode; + padapter->securitypriv.ndisauthtype = authmode; goto exit; } @@ -1881,7 +1881,7 @@ static int rtw_wx_set_enc(struct net_device *dev, padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; authmode = Ndis802_11AuthModeOpen; - padapter->securitypriv.ndisauthtype =authmode; + padapter->securitypriv.ndisauthtype = authmode; } else if (erq->flags & IW_ENCODE_RESTRICTED) { DBG_871X("rtw_wx_set_enc():IW_ENCODE_RESTRICTED\n"); padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled; @@ -1891,7 +1891,7 @@ static int rtw_wx_set_enc(struct net_device *dev, padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_; padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_; authmode = Ndis802_11AuthModeShared; - padapter->securitypriv.ndisauthtype =authmode; + padapter->securitypriv.ndisauthtype = authmode; } else { DBG_871X("rtw_wx_set_enc():erq->flags = 0x%x\n", erq->flags); @@ -1900,7 +1900,7 @@ static int rtw_wx_set_enc(struct net_device *dev, padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; authmode = Ndis802_11AuthModeOpen; - padapter->securitypriv.ndisauthtype =authmode; + padapter->securitypriv.ndisauthtype = authmode; } wep.KeyIndex = key; @@ -1909,7 +1909,7 @@ static int rtw_wx_set_enc(struct net_device *dev, wep.Length = wep.KeyLength + FIELD_OFFSET(struct ndis_802_11_wep, KeyMaterial); } else { - wep.KeyLength = 0 ; + wep.KeyLength = 0; if (keyindex_provided == 1) { /* set key_id only, no given KeyMaterial(erq->length == 0). */ padapter->securitypriv.dot11PrivacyKeyIndex = key; @@ -2093,7 +2093,7 @@ static int rtw_wx_set_auth(struct net_device *dev, padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */ - padapter->securitypriv.ndisauthtype =Ndis802_11AuthModeOpen; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen; } break; @@ -2181,7 +2181,7 @@ static int rtw_wx_set_enc_ext(struct net_device *dev, param->u.crypt.set_tx = 0; } - param->u.crypt.idx = (pencoding->flags&0x00FF) -1 ; + param->u.crypt.idx = (pencoding->flags & 0x00FF) - 1; if (pext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) memcpy(param->u.crypt.seq, pext->rx_seq, 8); @@ -2459,7 +2459,7 @@ static int rtw_get_ap_info(struct net_device *dev, /* pdata->length = 0;? */ pdata->flags = 0; - if (pdata->length>=32) { + if (pdata->length >= 32) { if (copy_from_user(data, pdata->pointer, 32)) { ret = -EINVAL; goto exit; @@ -2492,13 +2492,13 @@ static int rtw_get_ap_info(struct net_device *dev, DBG_871X("BSSID:" MAC_FMT "\n", MAC_ARG(bssid)); pbuf = rtw_get_wpa_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength-12); - if (pbuf && (wpa_ielen>0)) { + if (pbuf && (wpa_ielen > 0)) { pdata->flags = 1; break; } pbuf = rtw_get_wpa2_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength-12); - if (pbuf && (wpa_ielen>0)) { + if (pbuf && (wpa_ielen > 0)) { pdata->flags = 2; break; } @@ -2510,7 +2510,7 @@ static int rtw_get_ap_info(struct net_device *dev, spin_unlock_bh(&(pmlmepriv->scanned_queue.lock)); - if (pdata->length>=34) { + if (pdata->length >= 34) { if (copy_to_user((u8 __force __user *)pdata->pointer+32, (u8 *)&pdata->flags, 1)) { ret = -EINVAL; goto exit; @@ -2541,7 +2541,7 @@ static int rtw_set_pid(struct net_device *dev, selector = *pdata; if (selector < 3 && selector >= 0) { padapter->pid[selector] = *(pdata+1); - DBG_871X("%s set pid[%d]=%d\n", __func__, selector , padapter->pid[selector]); + DBG_871X("%s set pid[%d]=%d\n", __func__, selector, padapter->pid[selector]); } else DBG_871X("%s selector %d error\n", __func__, selector); @@ -2563,7 +2563,7 @@ static int rtw_wps_start(struct net_device *dev, u32 u32wps_start = 0; unsigned int uintRet = 0; - if ((true == padapter->bDriverStopped) ||(true ==padapter->bSurpriseRemoved) || (NULL == pdata)) { + if ((true == padapter->bDriverStopped) || (true == padapter->bSurpriseRemoved) || (NULL == pdata)) { ret = -EINVAL; goto exit; } @@ -2733,8 +2733,8 @@ static int rtw_dbg_port(struct net_device *dev, break; case 0x01: /* dbg mode */ padapter->recvpriv.is_signal_dbg = 1; - extra_arg = extra_arg>100?100:extra_arg; - padapter->recvpriv.signal_strength_dbg =extra_arg; + extra_arg = extra_arg > 100 ? 100 : extra_arg; + padapter->recvpriv.signal_strength_dbg = extra_arg; break; } break; @@ -2806,7 +2806,7 @@ static int rtw_dbg_port(struct net_device *dev, DBG_871X("ampdu_enable = %d\n", psta->htpriv.ampdu_enable); DBG_871X("agg_enable_bitmap =%x, candidate_tid_bitmap =%x\n", psta->htpriv.agg_enable_bitmap, psta->htpriv.candidate_tid_bitmap); - for (i = 0;i<16;i++) { + for (i = 0; i < 16; i++) { preorder_ctrl = &psta->recvreorder_ctrl[i]; if (preorder_ctrl->enable) DBG_871X("tid =%d, indicate_seq =%d\n", i, preorder_ctrl->indicate_seq); @@ -2845,7 +2845,7 @@ static int rtw_dbg_port(struct net_device *dev, spin_lock_bh(&pstapriv->sta_hash_lock); - for (i = 0; i< NUM_STA; i++) { + for (i = 0; i < NUM_STA; i++) { phead = &(pstapriv->sta_hash[i]); plist = get_next(phead); @@ -2872,7 +2872,7 @@ static int rtw_dbg_port(struct net_device *dev, - for (j = 0;j<16;j++) { + for (j = 0; j < 16; j++) { preorder_ctrl = &psta->recvreorder_ctrl[j]; if (preorder_ctrl->enable) DBG_871X("tid =%d, indicate_seq =%d\n", j, preorder_ctrl->indicate_seq); @@ -2900,7 +2900,7 @@ static int rtw_dbg_port(struct net_device *dev, DBG_871X("enable driver ctrl vcs = %d\n", extra_arg); padapter->driver_vcs_en = 1; - if (extra_arg>2) + if (extra_arg > 2) padapter->driver_vcs_type = 1; else padapter->driver_vcs_type = extra_arg; @@ -3048,7 +3048,7 @@ static int rtw_dbg_port(struct net_device *dev, if (max_rx_rate < 0xc) { /* max_rx_rate < MSC0 -> B or G -> disable HT */ pregistrypriv->ht_enable = 0; - for (i = 0; idatarate[i] > max_rx_rate) pmlmeext->datarate[i] = 0xff; } @@ -3057,7 +3057,7 @@ static int rtw_dbg_port(struct net_device *dev, else if (max_rx_rate < 0x1c) { /* mcs0~mcs15 */ u32 mcs_bitmap = 0x0; - for (i = 0; i<((max_rx_rate+1)-0xc); i++) + for (i = 0; i < ((max_rx_rate + 1) - 0xc); i++) mcs_bitmap |= BIT(i); set_mcs_rate_by_mask(pmlmeext->default_supported_mcs_set, mcs_bitmap); @@ -3129,9 +3129,9 @@ static int rtw_dbg_port(struct net_device *dev, */ int value; - DBG_871X("Set GPIO Direction! arg = %d , extra_arg =%d\n", arg , extra_arg); + DBG_871X("Set GPIO Direction! arg = %d , extra_arg =%d\n", arg, extra_arg); value = rtw_config_gpio(dev, arg, extra_arg); - DBG_871X("Set GPIO Direction %s\n", (value ==-1)?"Fail!!!":"Success"); + DBG_871X("Set GPIO Direction %s\n", (value == -1) ? "Fail!!!" : "Success"); break; } case 0x27: /* Set GPIO output direction value */ @@ -3142,15 +3142,15 @@ static int rtw_dbg_port(struct net_device *dev, */ int value; - DBG_871X("Set GPIO Value! arg = %d , extra_arg =%d\n", arg , extra_arg); + DBG_871X("Set GPIO Value! arg = %d , extra_arg =%d\n", arg, extra_arg); value = rtw_set_gpio_output_value(dev, arg, extra_arg); - DBG_871X("Set GPIO Value %s\n", (value ==-1)?"Fail!!!":"Success"); + DBG_871X("Set GPIO Value %s\n", (value == -1) ? "Fail!!!" : "Success"); break; } #endif case 0xaa: { - if ((extra_arg & 0x7F)> 0x3F) extra_arg = 0xFF; + if ((extra_arg & 0x7F) > 0x3F) extra_arg = 0xFF; DBG_871X("chang data rate to :0x%02x\n", extra_arg); padapter->fix_rate = extra_arg; } @@ -3161,7 +3161,7 @@ static int rtw_dbg_port(struct net_device *dev, mac_reg_dump(RTW_DBGDUMP, padapter); else if (extra_arg == 1) bb_reg_dump(RTW_DBGDUMP, padapter); - else if (extra_arg ==2) + else if (extra_arg == 2) rf_reg_dump(RTW_DBGDUMP, padapter); } break; @@ -3170,8 +3170,8 @@ static int rtw_dbg_port(struct net_device *dev, { u32 odm_flag; - if (0xf ==extra_arg) { - rtw_hal_get_def_var(padapter, HAL_DEF_DBG_DM_FUNC,&odm_flag); + if (0xf == extra_arg) { + rtw_hal_get_def_var(padapter, HAL_DEF_DBG_DM_FUNC, &odm_flag); DBG_871X(" === DMFlag(0x%08x) ===\n", odm_flag); DBG_871X("extra_arg = 0 - disable all dynamic func\n"); DBG_871X("extra_arg = 1 - disable DIG- BIT(0)\n"); @@ -3187,7 +3187,7 @@ static int rtw_dbg_port(struct net_device *dev, extra_arg = 3 - turn on all dynamic func */ rtw_hal_set_def_var(padapter, HAL_DEF_DBG_DM_FUNC, &(extra_arg)); - rtw_hal_get_def_var(padapter, HAL_DEF_DBG_DM_FUNC,&odm_flag); + rtw_hal_get_def_var(padapter, HAL_DEF_DBG_DM_FUNC, &odm_flag); DBG_871X(" === DMFlag(0x%08x) ===\n", odm_flag); } } @@ -3257,7 +3257,7 @@ static int wpa_set_param(struct net_device *dev, u8 name, u32 value) /* ret = ieee80211_wpa_enable(ieee, value); */ switch ((value)&0xff) { - case 1 : /* WPA */ + case 1: /* WPA */ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; /* WPA_PSK */ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled; break; @@ -3437,7 +3437,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, struct sta_info *psta = NULL, *pbcmc_sta = NULL; struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); struct mlme_priv *pmlmepriv = &padapter->mlmepriv; - struct security_priv* psecuritypriv =&(padapter->securitypriv); + struct security_priv* psecuritypriv = &(padapter->securitypriv); struct sta_priv *pstapriv = &padapter->stapriv; DBG_871X("%s\n", __func__); @@ -3490,7 +3490,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, DBG_871X("r871x_set_encryption, wep_key_idx =%d, len =%d\n", wep_key_idx, wep_key_len); - if ((wep_key_idx >= WEP_KEYS) || (wep_key_len<= 0)) { + if ((wep_key_idx >= WEP_KEYS) || (wep_key_len <= 0)) { ret = -EINVAL; goto exit; } @@ -3532,7 +3532,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), pwep->KeyMaterial, pwep->KeyLength); - psecuritypriv->dot11DefKeylen[wep_key_idx]=pwep->KeyLength; + psecuritypriv->dot11DefKeylen[wep_key_idx] = pwep->KeyLength; rtw_ap_set_wep_key(padapter, pwep->KeyMaterial, pwep->KeyLength, wep_key_idx, 1); } else { @@ -3558,7 +3558,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, if (strcmp(param->u.crypt.alg, "WEP") == 0) { DBG_871X("%s, set group_key, WEP\n", __func__); - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); psecuritypriv->dot118021XGrpPrivacy = _WEP40_; if (param->u.crypt.key_len == 13) @@ -3569,7 +3569,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, psecuritypriv->dot118021XGrpPrivacy = _TKIP_; - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ @@ -3584,7 +3584,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, psecuritypriv->dot118021XGrpPrivacy = _AES_; - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); } else { DBG_871X("%s, set group_key, none\n", __func__); @@ -3599,7 +3599,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, rtw_ap_set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx); - pbcmc_sta =rtw_get_bcmc_stainfo(padapter); + pbcmc_sta = rtw_get_bcmc_stainfo(padapter); if (pbcmc_sta) { pbcmc_sta->ieee8021x_blocked = false; pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy;/* rx will use bmc_sta's dot118021XPrivacy */ @@ -3613,7 +3613,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X && psta) { /* psk/802_1x */ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { if (param->u.crypt.set_tx == 1) { - memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); if (strcmp(param->u.crypt.alg, "WEP") == 0) { DBG_871X("%s, set pairwise key, WEP\n", __func__); @@ -3650,7 +3650,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, } else { /* group key??? */ if (strcmp(param->u.crypt.alg, "WEP") == 0) { - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); psecuritypriv->dot118021XGrpPrivacy = _WEP40_; if (param->u.crypt.key_len == 13) @@ -3658,7 +3658,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { psecuritypriv->dot118021XGrpPrivacy = _TKIP_; - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ /* set mic key */ @@ -3670,7 +3670,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { psecuritypriv->dot118021XGrpPrivacy = _AES_; - memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len>16 ?16:param->u.crypt.key_len)); + memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); } else { psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_; } @@ -3683,7 +3683,7 @@ static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, rtw_ap_set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx); - pbcmc_sta =rtw_get_bcmc_stainfo(padapter); + pbcmc_sta = rtw_get_bcmc_stainfo(padapter); if (pbcmc_sta) { pbcmc_sta->ieee8021x_blocked = false; pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy;/* rx will use bmc_sta's dot118021XPrivacy */ @@ -3715,7 +3715,7 @@ static int rtw_set_beacon(struct net_device *dev, struct ieee_param *param, int memcpy(&pstapriv->max_num_sta, param->u.bcn_ie.reserved, 2); - if ((pstapriv->max_num_sta>NUM_STA) || (pstapriv->max_num_sta<= 0)) + if ((pstapriv->max_num_sta > NUM_STA) || (pstapriv->max_num_sta <= 0)) pstapriv->max_num_sta = NUM_STA; @@ -3840,12 +3840,12 @@ static int rtw_del_sta(struct net_device *dev, struct ieee_param *param) psta = rtw_get_stainfo(pstapriv, param->sta_addr); if (psta) { - u8 updated =false; + u8 updated = false; /* DBG_871X("free psta =%p, aid =%d\n", psta, psta->aid); */ spin_lock_bh(&pstapriv->asoc_list_lock); - if (list_empty(&psta->asoc_list) ==false) { + if (list_empty(&psta->asoc_list) == false) { list_del_init(&psta->asoc_list); pstapriv->asoc_list_cnt--; updated = ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING); @@ -3904,12 +3904,12 @@ static int rtw_ioctl_get_sta_data(struct net_device *dev, struct ieee_param *par ht_20mhz_set : BIT(5) */ - psta_data->sta_set =((psta->nonerp_set) | - (psta->no_short_slot_time_set <<1) | - (psta->no_short_preamble_set <<2) | - (psta->no_ht_gf_set <<3) | - (psta->no_ht_set <<4) | - (psta->ht_20mhz_set <<5)); + psta_data->sta_set = ((psta->nonerp_set) | + (psta->no_short_slot_time_set << 1) | + (psta->no_short_preamble_set << 2) | + (psta->no_ht_gf_set << 3) | + (psta->no_ht_set << 4) | + (psta->ht_20mhz_set << 5)); psta_data->tx_supp_rates_len = psta->bssratelen; memcpy(psta_data->tx_supp_rates, psta->bssrateset, psta->bssratelen); @@ -3978,7 +3978,7 @@ static int rtw_get_sta_wpaie(struct net_device *dev, struct ieee_param *param) static int rtw_set_wps_beacon(struct net_device *dev, struct ieee_param *param, int len) { int ret = 0; - unsigned char wps_oui[4]={0x0, 0x50, 0xf2, 0x04}; + unsigned char wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev); struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); @@ -3995,7 +3995,7 @@ static int rtw_set_wps_beacon(struct net_device *dev, struct ieee_param *param, kfree(pmlmepriv->wps_beacon_ie); pmlmepriv->wps_beacon_ie = NULL; - if (ie_len>0) { + if (ie_len > 0) { pmlmepriv->wps_beacon_ie = rtw_malloc(ie_len); pmlmepriv->wps_beacon_ie_len = ie_len; if (pmlmepriv->wps_beacon_ie == NULL) { @@ -4033,7 +4033,7 @@ static int rtw_set_wps_probe_resp(struct net_device *dev, struct ieee_param *par kfree(pmlmepriv->wps_probe_resp_ie); pmlmepriv->wps_probe_resp_ie = NULL; - if (ie_len>0) { + if (ie_len > 0) { pmlmepriv->wps_probe_resp_ie = rtw_malloc(ie_len); pmlmepriv->wps_probe_resp_ie_len = ie_len; if (pmlmepriv->wps_probe_resp_ie == NULL) { @@ -4066,7 +4066,7 @@ static int rtw_set_wps_assoc_resp(struct net_device *dev, struct ieee_param *par kfree(pmlmepriv->wps_assoc_resp_ie); pmlmepriv->wps_assoc_resp_ie = NULL; - if (ie_len>0) { + if (ie_len > 0) { pmlmepriv->wps_assoc_resp_ie = rtw_malloc(ie_len); pmlmepriv->wps_assoc_resp_ie_len = ie_len; if (pmlmepriv->wps_assoc_resp_ie == NULL) { @@ -4380,11 +4380,11 @@ static int rtw_wx_set_priv(struct net_device *dev, struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); u8 *probereq_wpsie = ext; int probereq_wpsie_len = len; - u8 wps_oui[4]={0x0, 0x50, 0xf2, 0x04}; + u8 wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; if ((_VENDOR_SPECIFIC_IE_ == probereq_wpsie[0]) && (!memcmp(&probereq_wpsie[2], wps_oui, 4))) { - cp_sz = probereq_wpsie_len>MAX_WPS_IE_LEN ? MAX_WPS_IE_LEN:probereq_wpsie_len; + cp_sz = probereq_wpsie_len > MAX_WPS_IE_LEN ? MAX_WPS_IE_LEN : probereq_wpsie_len; if (pmlmepriv->wps_probe_req_ie) { pmlmepriv->wps_probe_req_ie_len = 0; @@ -4521,7 +4521,7 @@ static int rtw_test( ret = rtw_hal_fill_h2c_cmd(padapter, param[0], count-1, ¶m[1]); pos = sprintf(extra, "H2C ID = 0x%02x content =", param[0]); - for (i = 1; iiwstats; + struct iw_statistics *piwstats = &padapter->iwstats; int tmp_level = 0; int tmp_qual = 0; int tmp_noise = 0; @@ -4783,10 +4783,10 @@ static struct iw_statistics *rtw_get_wireless_stats(struct net_device *dev) rtw_ps_deny(padapter, PS_DENY_IOCTL); LeaveAllPowerSaveModeDirect(padapter); - rtw_hal_set_odm_var(padapter, HAL_ODM_NOISE_MONITOR,&info, false); + rtw_hal_set_odm_var(padapter, HAL_ODM_NOISE_MONITOR, &info, false); /* ODM_InbandNoise_Monitor(podmpriv, true, 0x20, 100); */ rtw_ps_deny_cancel(padapter, PS_DENY_IOCTL); - rtw_hal_get_odm_var(padapter, HAL_ODM_NOISE_MONITOR,&(info.chan), &(padapter->recvpriv.noise)); + rtw_hal_get_odm_var(padapter, HAL_ODM_NOISE_MONITOR, &(info.chan), &(padapter->recvpriv.noise)); DBG_871X("chan:%d, noise_level:%d\n", info.chan, padapter->recvpriv.noise); } #endif diff --git a/drivers/staging/rtl8723bs/os_dep/mlme_linux.c b/drivers/staging/rtl8723bs/os_dep/mlme_linux.c index 52a5b3156b28..f121dbfaa029 100644 --- a/drivers/staging/rtl8723bs/os_dep/mlme_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/mlme_linux.c @@ -64,7 +64,7 @@ void rtw_os_indicate_scan_done(struct adapter *padapter, bool aborted) indicate_wx_scan_complete_event(padapter); } -static RT_PMKID_LIST backupPMKIDList[ NUM_PMKID_CACHE ]; +static RT_PMKID_LIST backupPMKIDList[NUM_PMKID_CACHE]; void rtw_reset_securitypriv(struct adapter *adapter) { u8 backupPMKIDIndex = 0; @@ -83,7 +83,7 @@ void rtw_reset_securitypriv(struct adapter *adapter) /* Backup the btkip_countermeasure information. */ /* When the countermeasure is trigger, the driver have to disconnect with AP for 60 seconds. */ - memcpy(&backupPMKIDList[ 0 ], &adapter->securitypriv.PMKIDList[ 0 ], sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE); + memcpy(&backupPMKIDList[0], &adapter->securitypriv.PMKIDList[0], sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE); backupPMKIDIndex = adapter->securitypriv.PMKIDIndex; backupTKIPCountermeasure = adapter->securitypriv.btkip_countermeasure; backupTKIPcountermeasure_time = adapter->securitypriv.btkip_countermeasure_time; @@ -95,7 +95,7 @@ void rtw_reset_securitypriv(struct adapter *adapter) /* Added by Albert 2009/02/18 */ /* Restore the PMK information to securitypriv structure for the following connection. */ - memcpy(&adapter->securitypriv.PMKIDList[ 0 ], &backupPMKIDList[ 0 ], sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE); + memcpy(&adapter->securitypriv.PMKIDList[0], &backupPMKIDList[0], sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE); adapter->securitypriv.PMKIDIndex = backupPMKIDIndex; adapter->securitypriv.btkip_countermeasure = backupTKIPCountermeasure; adapter->securitypriv.btkip_countermeasure_time = backupTKIPcountermeasure_time; diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c index 47e984d5b7cb..17e65934fb5b 100644 --- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c +++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c @@ -202,8 +202,8 @@ module_param(rtw_tx_pwr_by_rate, int, 0644); MODULE_PARM_DESC(rtw_tx_pwr_by_rate, "0:Disable, 1:Enable, 2: Depend on efuse"); int _netdev_open(struct net_device *pnetdev); -int netdev_open (struct net_device *pnetdev); -static int netdev_close (struct net_device *pnetdev); +int netdev_open(struct net_device *pnetdev); +static int netdev_close(struct net_device *pnetdev); static void loadparam(struct adapter *padapter, _nic_hdl pnetdev) { @@ -221,7 +221,7 @@ static void loadparam(struct adapter *padapter, _nic_hdl pnetdev) registry_par->channel = (u8)rtw_channel; registry_par->wireless_mode = (u8)rtw_wireless_mode; - registry_par->vrtl_carrier_sense = (u8)rtw_vrtl_carrier_sense ; + registry_par->vrtl_carrier_sense = (u8)rtw_vrtl_carrier_sense; registry_par->vcs_type = (u8)rtw_vcs_type; registry_par->rts_thresh = (u16)rtw_rts_thresh; registry_par->frag_thresh = (u16)rtw_frag_thresh; @@ -554,7 +554,7 @@ u32 rtw_start_drv_threads(struct adapter *padapter) return _status; } -void rtw_stop_drv_threads (struct adapter *padapter) +void rtw_stop_drv_threads(struct adapter *padapter) { RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_stop_drv_threads\n")); @@ -1154,7 +1154,7 @@ void rtw_dev_unload(struct adapter *padapter) DBG_871X("stop cmdthd timeout\n"); break; } else { - cnt ++; + cnt++; DBG_871X("cmdthd is running(%d)\n", cnt); msleep(10); } @@ -1799,7 +1799,7 @@ int rtw_resume_common(struct adapter *padapter) pwrpriv->pno_in_resume = false; #endif } - DBG_871X_LEVEL(_drv_always_, "%s:%d in %d ms\n", __func__ , ret, + DBG_871X_LEVEL(_drv_always_, "%s:%d in %d ms\n", __func__, ret, jiffies_to_msecs(jiffies - start_time)); return ret; diff --git a/drivers/staging/rtl8723bs/os_dep/recv_linux.c b/drivers/staging/rtl8723bs/os_dep/recv_linux.c index 643caccc3069..22059bbbed9d 100644 --- a/drivers/staging/rtl8723bs/os_dep/recv_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/recv_linux.c @@ -197,7 +197,7 @@ void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup) key_type |= NL80211_KEYTYPE_PAIRWISE; } - cfg80211_michael_mic_failure(padapter->pnetdev, (u8 *)&pmlmepriv->assoc_bssid[ 0 ], key_type, -1, + cfg80211_michael_mic_failure(padapter->pnetdev, (u8 *)&pmlmepriv->assoc_bssid[0], key_type, -1, NULL, GFP_ATOMIC); memset(&ev, 0x00, sizeof(ev)); @@ -208,7 +208,7 @@ void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup) } ev.src_addr.sa_family = ARPHRD_ETHER; - memcpy(ev.src_addr.sa_data, &pmlmepriv->assoc_bssid[ 0 ], ETH_ALEN); + memcpy(ev.src_addr.sa_data, &pmlmepriv->assoc_bssid[0], ETH_ALEN); memset(&wrqu, 0x00, sizeof(wrqu)); wrqu.data.length = sizeof(ev); @@ -223,7 +223,7 @@ static void rtw_os_ksocket_send(struct adapter *padapter, union recv_frame *prec DBG_871X("eth rx: got eth_type = 0x%x\n", pattrib->eth_type); - if (psta && psta->isrc && psta->pid>0) { + if (psta && psta->isrc && psta->pid > 0) { u16 rx_pid; rx_pid = *(u16*)(skb->data+ETH_HLEN); @@ -239,7 +239,7 @@ static void rtw_os_ksocket_send(struct adapter *padapter, union recv_frame *prec /* DBG_871X("eth, RC: len = 0x%x, ctrl_type = 0x%x\n", len, ctrl_type); */ DBG_871X("eth, RC: len = 0x%x\n", len); - for (i = 0;idata+ETH_HLEN+4+i)); /* DBG_871X("0x%x\n", *(skb->data+ETH_HLEN+6+i)); */ diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c index 859f4a0afb95..b093b5629171 100644 --- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c +++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c @@ -108,7 +108,7 @@ static void sdio_free_irq(struct dvobj_priv *dvobj) err = sdio_release_irq(func); if (err) { dvobj->drv_dbg.dbg_sdio_free_irq_error_cnt++; - DBG_871X_LEVEL(_drv_err_,"%s: sdio_release_irq FAIL(%d)!\n", __func__, err); + DBG_871X_LEVEL(_drv_err_, "%s: sdio_release_irq FAIL(%d)!\n", __func__, err); } else dvobj->drv_dbg.dbg_sdio_free_irq_cnt++; sdio_release_host(func); @@ -324,7 +324,7 @@ static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct padapter->dvobj = dvobj; dvobj->if1 = padapter; - padapter->bDriverStopped =true; + padapter->bDriverStopped = true; dvobj->padapters = padapter; padapter->iface_id = 0; @@ -430,7 +430,7 @@ static void rtw_sdio_if1_deinit(struct adapter *if1) rtw_cancel_all_timer(if1); #ifdef CONFIG_WOWLAN - adapter_to_pwrctl(if1)->wowlan_mode =false; + adapter_to_pwrctl(if1)->wowlan_mode = false; DBG_871X_LEVEL(_drv_always_, "%s wowlan_mode:%d\n", __func__, adapter_to_pwrctl(if1)->wowlan_mode); #endif /* CONFIG_WOWLAN */ @@ -500,7 +500,7 @@ free_dvobj: if (status != _SUCCESS) sdio_dvobj_deinit(func); exit: - return status == _SUCCESS?0:-ENODEV; + return status == _SUCCESS ? 0 : -ENODEV; } static void rtw_dev_remove(struct sdio_func *func) @@ -548,7 +548,7 @@ extern int pm_netdev_close(struct net_device *pnetdev, u8 bnormal); static int rtw_sdio_suspend(struct device *dev) { - struct sdio_func *func =dev_to_sdio_func(dev); + struct sdio_func *func = dev_to_sdio_func(dev); struct dvobj_priv *psdpriv = sdio_get_drvdata(func); struct pwrctrl_priv *pwrpriv = dvobj_to_pwrctl(psdpriv); struct adapter *padapter = psdpriv->if1; @@ -585,7 +585,7 @@ static int rtw_resume_process(struct adapter *padapter) static int rtw_sdio_resume(struct device *dev) { - struct sdio_func *func =dev_to_sdio_func(dev); + struct sdio_func *func = dev_to_sdio_func(dev); struct dvobj_priv *psdpriv = sdio_get_drvdata(func); struct adapter *padapter = psdpriv->if1; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; diff --git a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c index 4e81bc13e57d..fec8a8caaa46 100644 --- a/drivers/staging/rtl8723bs/os_dep/xmit_linux.c +++ b/drivers/staging/rtl8723bs/os_dep/xmit_linux.c @@ -15,16 +15,16 @@ uint rtw_remainder_len(struct pkt_file *pfile) return (pfile->buf_len - ((SIZE_PTR)(pfile->cur_addr) - (SIZE_PTR)(pfile->buf_start))); } -void _rtw_open_pktfile (_pkt *pktptr, struct pkt_file *pfile) +void _rtw_open_pktfile(_pkt *pktptr, struct pkt_file *pfile) { pfile->pkt = pktptr; pfile->cur_addr = pfile->buf_start = pktptr->data; pfile->pkt_len = pfile->buf_len = pktptr->len; - pfile->cur_buffer = pfile->buf_start ; + pfile->cur_buffer = pfile->buf_start; } -uint _rtw_pktfile_read (struct pkt_file *pfile, u8 *rmem, uint rlen) +uint _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen) { uint len = 0; From 38f193dff5cc8e065900fe591c48fdb084633a09 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 14 Feb 2020 11:52:15 +0000 Subject: [PATCH 0240/2231] staging: wilc1000: make use of FIELD_GET/_PREP macro Simplified the code by making use of FIELD_GET/_PREP bitfield macro. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200214172250.13026-2-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/cfg80211.c | 2 +- drivers/staging/wilc1000/mon.c | 2 +- drivers/staging/wilc1000/netdev.h | 2 -- drivers/staging/wilc1000/spi.c | 2 +- drivers/staging/wilc1000/wlan.c | 32 +++++++++++++++-------------- drivers/staging/wilc1000/wlan.h | 20 +++++++++++++++++- 6 files changed, 39 insertions(+), 21 deletions(-) diff --git a/drivers/staging/wilc1000/cfg80211.c b/drivers/staging/wilc1000/cfg80211.c index d9c7bed2e6fb..995b1f306807 100644 --- a/drivers/staging/wilc1000/cfg80211.c +++ b/drivers/staging/wilc1000/cfg80211.c @@ -988,7 +988,7 @@ void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size) s32 freq; header = get_unaligned_le32(buff - HOST_HDR_OFFSET); - pkt_offset = GET_PKT_OFFSET(header); + pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header); if (pkt_offset & IS_MANAGMEMENT_CALLBACK) { bool ack = false; diff --git a/drivers/staging/wilc1000/mon.c b/drivers/staging/wilc1000/mon.c index 48ac33f06f63..60331417bd98 100644 --- a/drivers/staging/wilc1000/mon.c +++ b/drivers/staging/wilc1000/mon.c @@ -40,7 +40,7 @@ void wilc_wfi_monitor_rx(struct net_device *mon_dev, u8 *buff, u32 size) * The packet offset field contain info about what type of management * the frame we are dealing with and ack status */ - pkt_offset = GET_PKT_OFFSET(header); + pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header); if (pkt_offset & IS_MANAGMEMENT_CALLBACK) { /* hostapd callback mgmt frame */ diff --git a/drivers/staging/wilc1000/netdev.h b/drivers/staging/wilc1000/netdev.h index c475e78e3a69..e3689e2a4abb 100644 --- a/drivers/staging/wilc1000/netdev.h +++ b/drivers/staging/wilc1000/netdev.h @@ -29,8 +29,6 @@ #define TCP_ACK_FILTER_LINK_SPEED_THRESH 54 #define DEFAULT_LINK_SPEED 72 -#define GET_PKT_OFFSET(a) (((a) >> 22) & 0x1ff) - struct wilc_wfi_stats { unsigned long rx_packets; unsigned long tx_packets; diff --git a/drivers/staging/wilc1000/spi.c b/drivers/staging/wilc1000/spi.c index 9a9f3624e158..300c5c832572 100644 --- a/drivers/staging/wilc1000/spi.c +++ b/drivers/staging/wilc1000/spi.c @@ -898,7 +898,7 @@ static int wilc_spi_read_size(struct wilc *wilc, u32 *size) int ret; ret = spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE, size); - *size = *size & IRQ_DMA_WD_CNT_MASK; + *size = FIELD_GET(IRQ_DMA_WD_CNT_MASK, *size); return ret; } diff --git a/drivers/staging/wilc1000/wlan.c b/drivers/staging/wilc1000/wlan.c index 601e4d1345d2..9dfabd1af4e7 100644 --- a/drivers/staging/wilc1000/wlan.c +++ b/drivers/staging/wilc1000/wlan.c @@ -568,8 +568,8 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, ®); if (ret) break; - if ((reg >> 2) & 0x1) { - entries = ((reg >> 3) & 0x3f); + if (FIELD_GET(WILC_VMM_ENTRY_AVAILABLE, reg)) { + entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, reg); break; } release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP); @@ -610,6 +610,7 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) do { u32 header, buffer_offset; char *bssid; + u8 mgmt_ptk = 0; tqe = wilc_wlan_txq_remove_from_head(dev); if (!tqe) @@ -620,15 +621,16 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) break; le32_to_cpus(&vmm_table[i]); - vmm_sz = (vmm_table[i] & 0x3ff); + vmm_sz = FIELD_GET(WILC_VMM_BUFFER_SIZE, vmm_table[i]); vmm_sz *= 4; - header = (tqe->type << 31) | - (tqe->buffer_size << 15) | - vmm_sz; + if (tqe->type == WILC_MGMT_PKT) - header |= BIT(30); - else - header &= ~BIT(30); + mgmt_ptk = 1; + + header = (FIELD_PREP(WILC_VMM_HDR_TYPE, tqe->type) | + FIELD_PREP(WILC_VMM_HDR_MGMT_FIELD, mgmt_ptk) | + FIELD_PREP(WILC_VMM_HDR_PKT_SIZE, tqe->buffer_size) | + FIELD_PREP(WILC_VMM_HDR_BUFF_SIZE, vmm_sz)); cpu_to_le32s(&header); memcpy(&txb[offset], &header, 4); @@ -686,10 +688,10 @@ static void wilc_wlan_handle_rx_buff(struct wilc *wilc, u8 *buffer, int size) buff_ptr = buffer + offset; header = get_unaligned_le32(buff_ptr); - is_cfg_packet = (header >> 31) & 0x1; - pkt_offset = (header >> 22) & 0x1ff; - tp_len = (header >> 11) & 0x7ff; - pkt_len = header & 0x7ff; + is_cfg_packet = FIELD_GET(WILC_PKT_HDR_CONFIG_FIELD, header); + pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header); + tp_len = FIELD_GET(WILC_PKT_HDR_TOTAL_LEN_FIELD, header); + pkt_len = FIELD_GET(WILC_PKT_HDR_LEN_FIELD, header); if (pkt_len == 0 || tp_len == 0) break; @@ -758,11 +760,11 @@ static void wilc_wlan_handle_isr_ext(struct wilc *wilc, u32 int_status) int ret = 0; struct rxq_entry_t *rqe; - size = (int_status & 0x7fff) << 2; + size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, int_status) << 2; while (!size && retries < 10) { wilc->hif_func->hif_read_size(wilc, &size); - size = (size & 0x7fff) << 2; + size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, size) << 2; retries++; } diff --git a/drivers/staging/wilc1000/wlan.h b/drivers/staging/wilc1000/wlan.h index 8c4634262adb..d3e822c374eb 100644 --- a/drivers/staging/wilc1000/wlan.h +++ b/drivers/staging/wilc1000/wlan.h @@ -8,6 +8,7 @@ #define WILC_WLAN_H #include +#include /******************************************** * @@ -135,6 +136,23 @@ #define MODALIAS "WILC_SPI" #define GPIO_NUM 0x44 + +#define WILC_PKT_HDR_CONFIG_FIELD BIT(31) +#define WILC_PKT_HDR_OFFSET_FIELD GENMASK(30, 22) +#define WILC_PKT_HDR_TOTAL_LEN_FIELD GENMASK(21, 11) +#define WILC_PKT_HDR_LEN_FIELD GENMASK(10, 0) + +#define WILC_INTERRUPT_DATA_SIZE GENMASK(14, 0) + +#define WILC_VMM_BUFFER_SIZE GENMASK(9, 0) + +#define WILC_VMM_HDR_TYPE BIT(31) +#define WILC_VMM_HDR_MGMT_FIELD BIT(30) +#define WILC_VMM_HDR_PKT_SIZE GENMASK(29, 15) +#define WILC_VMM_HDR_BUFF_SIZE GENMASK(14, 0) + +#define WILC_VMM_ENTRY_COUNT GENMASK(8, 3) +#define WILC_VMM_ENTRY_AVAILABLE BIT(2) /*******************************************/ /* E0 and later Interrupt flags. */ /*******************************************/ @@ -150,7 +168,7 @@ /* 21: INT5 flag */ /*******************************************/ #define IRG_FLAGS_OFFSET 16 -#define IRQ_DMA_WD_CNT_MASK ((1ul << IRG_FLAGS_OFFSET) - 1) +#define IRQ_DMA_WD_CNT_MASK GENMASK(IRG_FLAGS_OFFSET - 1, 0) #define INT_0 BIT(IRG_FLAGS_OFFSET) #define INT_1 BIT(IRG_FLAGS_OFFSET + 1) #define INT_2 BIT(IRG_FLAGS_OFFSET + 2) From 759af9b84eec238cee2d0e5b42638f6f046b51d3 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 14 Feb 2020 11:52:16 +0000 Subject: [PATCH 0241/2231] staging: wilc1000: remove use of MAX_NUN_INT_THRPT_ENH2 macro Make use of MAX_NUM_INT existing macro to handle the maximum supported interrupts count and removed MAX_NUN_INT_THRPT_ENH2 macro. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200214172250.13026-3-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/sdio.c | 20 +++----------------- drivers/staging/wilc1000/wlan.h | 2 +- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/staging/wilc1000/sdio.c b/drivers/staging/wilc1000/sdio.c index ca99335687c4..77ef84f9cc37 100644 --- a/drivers/staging/wilc1000/sdio.c +++ b/drivers/staging/wilc1000/sdio.c @@ -27,8 +27,6 @@ struct wilc_sdio { bool irq_gpio; u32 block_size; int nint; -/* Max num interrupts allowed in registers 0xf7, 0xf8 */ -#define MAX_NUN_INT_THRPT_ENH2 (5) int has_thrpt_enh3; }; @@ -818,8 +816,6 @@ static int wilc_sdio_read_int(struct wilc *wilc, u32 *int_status) tmp |= INT_3; if (cmd.data & BIT(5)) tmp |= INT_4; - if (cmd.data & BIT(6)) - tmp |= INT_5; for (i = sdio_priv->nint; i < MAX_NUM_INT; i++) { if ((tmp >> (IRG_FLAGS_OFFSET + i)) & 0x1) { dev_err(&func->dev, @@ -854,16 +850,11 @@ static int wilc_sdio_clear_int_ext(struct wilc *wilc, u32 val) int vmm_ctl; if (sdio_priv->has_thrpt_enh3) { - u32 reg; + u32 reg = 0; - if (sdio_priv->irq_gpio) { - u32 flags; + if (sdio_priv->irq_gpio) + reg = val & (BIT(MAX_NUM_INT) - 1); - flags = val & (BIT(MAX_NUN_INT_THRPT_ENH2) - 1); - reg = flags; - } else { - reg = 0; - } /* select VMM table 0 */ if (val & SEL_VMM_TBL0) reg |= BIT(5); @@ -975,11 +966,6 @@ static int wilc_sdio_sync_ext(struct wilc *wilc, int nint) dev_err(&func->dev, "Too many interrupts (%d)...\n", nint); return -EINVAL; } - if (nint > MAX_NUN_INT_THRPT_ENH2) { - dev_err(&func->dev, - "Cannot support more than 5 interrupts when has_thrpt_enh2=1.\n"); - return -EINVAL; - } sdio_priv->nint = nint; diff --git a/drivers/staging/wilc1000/wlan.h b/drivers/staging/wilc1000/wlan.h index d3e822c374eb..876b02e93a81 100644 --- a/drivers/staging/wilc1000/wlan.h +++ b/drivers/staging/wilc1000/wlan.h @@ -175,7 +175,7 @@ #define INT_3 BIT(IRG_FLAGS_OFFSET + 3) #define INT_4 BIT(IRG_FLAGS_OFFSET + 4) #define INT_5 BIT(IRG_FLAGS_OFFSET + 5) -#define MAX_NUM_INT 6 +#define MAX_NUM_INT 5 /*******************************************/ /* E0 and later Interrupt flags. */ From eda308be643f70dfec22d6fb7c6de643bc474d35 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 14 Feb 2020 11:52:17 +0000 Subject: [PATCH 0242/2231] staging: wilc1000: refactor interrupt handling for sdio Make use of FIELD_PREP/FIELD_GET macro to refactor the interrupt handling for SDIO. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200214172250.13026-4-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/sdio.c | 98 ++++++++++++--------------------- drivers/staging/wilc1000/spi.c | 4 -- drivers/staging/wilc1000/wlan.h | 3 + 3 files changed, 39 insertions(+), 66 deletions(-) diff --git a/drivers/staging/wilc1000/sdio.c b/drivers/staging/wilc1000/sdio.c index 77ef84f9cc37..66706efc5711 100644 --- a/drivers/staging/wilc1000/sdio.c +++ b/drivers/staging/wilc1000/sdio.c @@ -26,7 +26,6 @@ static const struct sdio_device_id wilc_sdio_ids[] = { struct wilc_sdio { bool irq_gpio; u32 block_size; - int nint; int has_thrpt_enh3; }; @@ -790,6 +789,7 @@ static int wilc_sdio_read_int(struct wilc *wilc, u32 *int_status) struct sdio_func *func = dev_to_sdio_func(wilc->dev); struct wilc_sdio *sdio_priv = wilc->bus_data; u32 tmp; + u8 irq_flags; struct sdio_cmd52 cmd; wilc_sdio_read_size(wilc, &tmp); @@ -798,44 +798,22 @@ static int wilc_sdio_read_int(struct wilc *wilc, u32 *int_status) * Read IRQ flags **/ if (!sdio_priv->irq_gpio) { - int i; - - cmd.read_write = 0; cmd.function = 1; cmd.address = 0x04; - cmd.data = 0; - wilc_sdio_cmd52(wilc, &cmd); - - if (cmd.data & BIT(0)) - tmp |= INT_0; - if (cmd.data & BIT(2)) - tmp |= INT_1; - if (cmd.data & BIT(3)) - tmp |= INT_2; - if (cmd.data & BIT(4)) - tmp |= INT_3; - if (cmd.data & BIT(5)) - tmp |= INT_4; - for (i = sdio_priv->nint; i < MAX_NUM_INT; i++) { - if ((tmp >> (IRG_FLAGS_OFFSET + i)) & 0x1) { - dev_err(&func->dev, - "Unexpected interrupt (1) : tmp=%x, data=%x\n", - tmp, cmd.data); - break; - } - } } else { - u32 irq_flags; - - cmd.read_write = 0; cmd.function = 0; - cmd.raw = 0; cmd.address = 0xf7; - cmd.data = 0; - wilc_sdio_cmd52(wilc, &cmd); - irq_flags = cmd.data & 0x1f; - tmp |= ((irq_flags >> 0) << IRG_FLAGS_OFFSET); } + cmd.raw = 0; + cmd.read_write = 0; + cmd.data = 0; + wilc_sdio_cmd52(wilc, &cmd); + irq_flags = cmd.data; + tmp |= FIELD_PREP(IRG_FLAGS_MASK, cmd.data); + + if (FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags)) + dev_err(&func->dev, "Unexpected interrupt (1) int=%lx\n", + FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags)); *int_status = tmp; @@ -890,38 +868,36 @@ static int wilc_sdio_clear_int_ext(struct wilc *wilc, u32 val) * Must clear each interrupt individually. */ u32 flags; + int i; flags = val & (BIT(MAX_NUM_INT) - 1); - if (flags) { - int i; + for (i = 0; i < NUM_INT_EXT && flags; i++) { + if (flags & BIT(i)) { + struct sdio_cmd52 cmd; - for (i = 0; i < sdio_priv->nint; i++) { - if (flags & 1) { - struct sdio_cmd52 cmd; + cmd.read_write = 1; + cmd.function = 0; + cmd.raw = 0; + cmd.address = 0xf8; + cmd.data = BIT(i); - cmd.read_write = 1; - cmd.function = 0; - cmd.raw = 0; - cmd.address = 0xf8; - cmd.data = BIT(i); - - ret = wilc_sdio_cmd52(wilc, &cmd); - if (ret) { - dev_err(&func->dev, - "Failed cmd52, set 0xf8 data (%d) ...\n", - __LINE__); - return ret; - } - } - flags >>= 1; - } - - for (i = sdio_priv->nint; i < MAX_NUM_INT; i++) { - if (flags & 1) + ret = wilc_sdio_cmd52(wilc, &cmd); + if (ret) { dev_err(&func->dev, - "Unexpected interrupt cleared %d...\n", - i); - flags >>= 1; + "Failed cmd52, set 0xf8 data (%d) ...\n", + __LINE__); + return ret; + } + flags &= ~BIT(i); + } + } + + for (i = NUM_INT_EXT; i < MAX_NUM_INT && flags; i++) { + if (flags & BIT(i)) { + dev_err(&func->dev, + "Unexpected interrupt cleared %d...\n", + i); + flags &= ~BIT(i); } } } @@ -967,8 +943,6 @@ static int wilc_sdio_sync_ext(struct wilc *wilc, int nint) return -EINVAL; } - sdio_priv->nint = nint; - /** * Disable power sequencer **/ diff --git a/drivers/staging/wilc1000/spi.c b/drivers/staging/wilc1000/spi.c index 300c5c832572..37be627e72ce 100644 --- a/drivers/staging/wilc1000/spi.c +++ b/drivers/staging/wilc1000/spi.c @@ -12,7 +12,6 @@ struct wilc_spi { int crc_off; - int nint; }; static const struct wilc_hif_func wilc_hif_spi; @@ -916,7 +915,6 @@ static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val) static int wilc_spi_sync_ext(struct wilc *wilc, int nint) { struct spi_device *spi = to_spi_device(wilc->dev); - struct wilc_spi *spi_priv = wilc->bus_data; u32 reg; int ret, i; @@ -925,8 +923,6 @@ static int wilc_spi_sync_ext(struct wilc *wilc, int nint) return -EINVAL; } - spi_priv->nint = nint; - /* * interrupt pin mux select */ diff --git a/drivers/staging/wilc1000/wlan.h b/drivers/staging/wilc1000/wlan.h index 876b02e93a81..27f08b375345 100644 --- a/drivers/staging/wilc1000/wlan.h +++ b/drivers/staging/wilc1000/wlan.h @@ -176,6 +176,8 @@ #define INT_4 BIT(IRG_FLAGS_OFFSET + 4) #define INT_5 BIT(IRG_FLAGS_OFFSET + 5) #define MAX_NUM_INT 5 +#define IRG_FLAGS_MASK GENMASK(IRG_FLAGS_OFFSET + MAX_NUM_INT, \ + IRG_FLAGS_OFFSET) /*******************************************/ /* E0 and later Interrupt flags. */ @@ -203,6 +205,7 @@ #define DATA_INT_EXT INT_0 #define ALL_INT_EXT DATA_INT_EXT #define NUM_INT_EXT 1 +#define UNHANDLED_IRQ_MASK GENMASK(MAX_NUM_INT - 1, NUM_INT_EXT) #define DATA_INT_CLR CLR_INT0 From bf13984d515d83a40e2686358a641b04f9c2bfc0 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 14 Feb 2020 11:52:18 +0000 Subject: [PATCH 0243/2231] staging: wilc1000: make use of ALIGN macro Make use of 'ALIGN' macro to align the size data value. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200214172250.13026-5-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/sdio.c | 34 ++------------------------------- drivers/staging/wilc1000/wlan.c | 4 +--- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/drivers/staging/wilc1000/sdio.c b/drivers/staging/wilc1000/sdio.c index 66706efc5711..212affc4b9c1 100644 --- a/drivers/staging/wilc1000/sdio.c +++ b/drivers/staging/wilc1000/sdio.c @@ -415,28 +415,12 @@ static int wilc_sdio_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size) cmd.read_write = 1; if (addr > 0) { - /** - * has to be word aligned... - **/ - if (size & 0x3) { - size += 4; - size &= ~0x3; - } - /** * func 0 access **/ cmd.function = 0; cmd.address = 0x10f; } else { - /** - * has to be word aligned... - **/ - if (size & 0x3) { - size += 4; - size &= ~0x3; - } - /** * func 1 access **/ @@ -444,6 +428,7 @@ static int wilc_sdio_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size) cmd.address = 0; } + size = ALIGN(size, 4); nblk = size / block_size; nleft = size % block_size; @@ -551,28 +536,12 @@ static int wilc_sdio_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size) cmd.read_write = 0; if (addr > 0) { - /** - * has to be word aligned... - **/ - if (size & 0x3) { - size += 4; - size &= ~0x3; - } - /** * func 0 access **/ cmd.function = 0; cmd.address = 0x10f; } else { - /** - * has to be word aligned... - **/ - if (size & 0x3) { - size += 4; - size &= ~0x3; - } - /** * func 1 access **/ @@ -580,6 +549,7 @@ static int wilc_sdio_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size) cmd.address = 0; } + size = ALIGN(size, 4); nblk = size / block_size; nleft = size % block_size; diff --git a/drivers/staging/wilc1000/wlan.c b/drivers/staging/wilc1000/wlan.c index 9dfabd1af4e7..db4ef175ccee 100644 --- a/drivers/staging/wilc1000/wlan.c +++ b/drivers/staging/wilc1000/wlan.c @@ -508,9 +508,7 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) vmm_sz = HOST_HDR_OFFSET; vmm_sz += tqe->buffer_size; - - if (vmm_sz & 0x3) - vmm_sz = (vmm_sz + 4) & ~0x3; + vmm_sz = ALIGN(vmm_sz, 4); if ((sum + vmm_sz) > WILC_TX_BUFF_SIZE) break; From a088ae77f44b13a4b74c7e0be00fcfb43379a6dd Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 14 Feb 2020 11:52:20 +0000 Subject: [PATCH 0244/2231] staging: wilc1000: use commmon function to set SDIO block size Use common function wilc_sdio_set_block_size() to set the block size for SDIO. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200214172250.13026-6-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/sdio.c | 51 +++++++-------------------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/drivers/staging/wilc1000/sdio.c b/drivers/staging/wilc1000/sdio.c index 212affc4b9c1..57c0e8415bc9 100644 --- a/drivers/staging/wilc1000/sdio.c +++ b/drivers/staging/wilc1000/sdio.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "netdev.h" #include "cfg80211.h" @@ -292,7 +293,8 @@ static int wilc_sdio_set_func0_csa_address(struct wilc *wilc, u32 adr) return 0; } -static int wilc_sdio_set_func0_block_size(struct wilc *wilc, u32 block_size) +static int wilc_sdio_set_block_size(struct wilc *wilc, u8 func_num, + u32 block_size) { struct sdio_func *func = dev_to_sdio_func(wilc->dev); struct sdio_cmd52 cmd; @@ -301,52 +303,21 @@ static int wilc_sdio_set_func0_block_size(struct wilc *wilc, u32 block_size) cmd.read_write = 1; cmd.function = 0; cmd.raw = 0; - cmd.address = 0x10; + cmd.address = SDIO_FBR_BASE(func_num) + SDIO_CCCR_BLKSIZE; cmd.data = (u8)block_size; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { - dev_err(&func->dev, "Failed cmd52, set 0x10 data...\n"); + dev_err(&func->dev, "Failed cmd52, set %04x data...\n", + cmd.address); return ret; } - cmd.address = 0x11; + cmd.address = SDIO_FBR_BASE(func_num) + SDIO_CCCR_BLKSIZE + 1; cmd.data = (u8)(block_size >> 8); ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { - dev_err(&func->dev, "Failed cmd52, set 0x11 data...\n"); - return ret; - } - - return 0; -} - -/******************************************** - * - * Function 1 - * - ********************************************/ - -static int wilc_sdio_set_func1_block_size(struct wilc *wilc, u32 block_size) -{ - struct sdio_func *func = dev_to_sdio_func(wilc->dev); - struct sdio_cmd52 cmd; - int ret; - - cmd.read_write = 1; - cmd.function = 0; - cmd.raw = 0; - cmd.address = 0x110; - cmd.data = (u8)block_size; - ret = wilc_sdio_cmd52(wilc, &cmd); - if (ret) { - dev_err(&func->dev, "Failed cmd52, set 0x110 data...\n"); - return ret; - } - cmd.address = 0x111; - cmd.data = (u8)(block_size >> 8); - ret = wilc_sdio_cmd52(wilc, &cmd); - if (ret) { - dev_err(&func->dev, "Failed cmd52, set 0x111 data...\n"); + dev_err(&func->dev, "Failed cmd52, set %04x data...\n", + cmd.address); return ret; } @@ -638,7 +609,7 @@ static int wilc_sdio_init(struct wilc *wilc, bool resume) /** * function 0 block size **/ - ret = wilc_sdio_set_func0_block_size(wilc, WILC_SDIO_BLOCK_SIZE); + ret = wilc_sdio_set_block_size(wilc, 0, WILC_SDIO_BLOCK_SIZE); if (ret) { dev_err(&func->dev, "Fail cmd 52, set func 0 block size...\n"); return ret; @@ -688,7 +659,7 @@ static int wilc_sdio_init(struct wilc *wilc, bool resume) /** * func 1 is ready, set func 1 block size **/ - ret = wilc_sdio_set_func1_block_size(wilc, WILC_SDIO_BLOCK_SIZE); + ret = wilc_sdio_set_block_size(wilc, 1, WILC_SDIO_BLOCK_SIZE); if (ret) { dev_err(&func->dev, "Fail set func 1 block size...\n"); return ret; From 167bd21c315e906ec12d4194e9ca34760256332c Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 14 Feb 2020 11:52:21 +0000 Subject: [PATCH 0245/2231] staging: wilc1000: define macros for different register address for SDIO Instead of using hardcoded value for SDIO register address added the macro for them. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200214172250.13026-7-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/sdio.c | 77 +++++++++++++++++---------------- drivers/staging/wilc1000/wlan.h | 36 +++++++++++++++ 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/drivers/staging/wilc1000/sdio.c b/drivers/staging/wilc1000/sdio.c index 57c0e8415bc9..0670c5956aef 100644 --- a/drivers/staging/wilc1000/sdio.c +++ b/drivers/staging/wilc1000/sdio.c @@ -183,8 +183,8 @@ static int wilc_sdio_reset(struct wilc *wilc) cmd.read_write = 1; cmd.function = 0; cmd.raw = 0; - cmd.address = 0x6; - cmd.data = 0x8; + cmd.address = SDIO_CCCR_ABORT; + cmd.data = WILC_SDIO_CCCR_ABORT_RESET; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, "Fail cmd 52, reset cmd ...\n"); @@ -266,27 +266,30 @@ static int wilc_sdio_set_func0_csa_address(struct wilc *wilc, u32 adr) cmd.read_write = 1; cmd.function = 0; cmd.raw = 0; - cmd.address = 0x10c; + cmd.address = WILC_SDIO_FBR_CSA_REG; cmd.data = (u8)adr; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { - dev_err(&func->dev, "Failed cmd52, set 0x10c data...\n"); + dev_err(&func->dev, "Failed cmd52, set %04x data...\n", + cmd.address); return ret; } - cmd.address = 0x10d; + cmd.address = WILC_SDIO_FBR_CSA_REG + 1; cmd.data = (u8)(adr >> 8); ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { - dev_err(&func->dev, "Failed cmd52, set 0x10d data...\n"); + dev_err(&func->dev, "Failed cmd52, set %04x data...\n", + cmd.address); return ret; } - cmd.address = 0x10e; + cmd.address = WILC_SDIO_FBR_CSA_REG + 2; cmd.data = (u8)(adr >> 16); ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { - dev_err(&func->dev, "Failed cmd52, set 0x10e data...\n"); + dev_err(&func->dev, "Failed cmd52, set %04x data...\n", + cmd.address); return ret; } @@ -337,7 +340,7 @@ static int wilc_sdio_write_reg(struct wilc *wilc, u32 addr, u32 data) cpu_to_le32s(&data); - if (addr >= 0xf0 && addr <= 0xff) { + if (addr >= 0xf0 && addr <= 0xff) { /* only vendor specific registers */ struct sdio_cmd52 cmd; cmd.read_write = 1; @@ -361,7 +364,7 @@ static int wilc_sdio_write_reg(struct wilc *wilc, u32 addr, u32 data) cmd.read_write = 1; cmd.function = 0; - cmd.address = 0x10f; + cmd.address = WILC_SDIO_FBR_DATA_REG; cmd.block_mode = 0; cmd.increment = 1; cmd.count = 4; @@ -390,13 +393,13 @@ static int wilc_sdio_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size) * func 0 access **/ cmd.function = 0; - cmd.address = 0x10f; + cmd.address = WILC_SDIO_FBR_DATA_REG; } else { /** * func 1 access **/ cmd.function = 1; - cmd.address = 0; + cmd.address = WILC_SDIO_F1_DATA_REG; } size = ALIGN(size, 4); @@ -455,7 +458,7 @@ static int wilc_sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data) struct wilc_sdio *sdio_priv = wilc->bus_data; int ret; - if (addr >= 0xf0 && addr <= 0xff) { + if (addr >= 0xf0 && addr <= 0xff) { /* only vendor specific registers */ struct sdio_cmd52 cmd; cmd.read_write = 0; @@ -478,7 +481,7 @@ static int wilc_sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data) cmd.read_write = 0; cmd.function = 0; - cmd.address = 0x10f; + cmd.address = WILC_SDIO_FBR_DATA_REG; cmd.block_mode = 0; cmd.increment = 1; cmd.count = 4; @@ -511,13 +514,13 @@ static int wilc_sdio_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size) * func 0 access **/ cmd.function = 0; - cmd.address = 0x10f; + cmd.address = WILC_SDIO_FBR_DATA_REG; } else { /** * func 1 access **/ cmd.function = 1; - cmd.address = 0; + cmd.address = WILC_SDIO_F1_DATA_REG; } size = ALIGN(size, 4); @@ -598,8 +601,8 @@ static int wilc_sdio_init(struct wilc *wilc, bool resume) cmd.read_write = 1; cmd.function = 0; cmd.raw = 1; - cmd.address = 0x100; - cmd.data = 0x80; + cmd.address = SDIO_FBR_BASE(func->num); + cmd.data = SDIO_FBR_ENABLE_CSA; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, "Fail cmd 52, enable csa...\n"); @@ -622,8 +625,8 @@ static int wilc_sdio_init(struct wilc *wilc, bool resume) cmd.read_write = 1; cmd.function = 0; cmd.raw = 1; - cmd.address = 0x2; - cmd.data = 0x2; + cmd.address = SDIO_CCCR_IOEx; + cmd.data = WILC_SDIO_CCCR_IO_EN_FUNC1; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, @@ -637,7 +640,7 @@ static int wilc_sdio_init(struct wilc *wilc, bool resume) cmd.read_write = 0; cmd.function = 0; cmd.raw = 0; - cmd.address = 0x3; + cmd.address = SDIO_CCCR_IORx; loop = 3; do { cmd.data = 0; @@ -647,7 +650,7 @@ static int wilc_sdio_init(struct wilc *wilc, bool resume) "Fail cmd 52, get IOR register...\n"); return ret; } - if (cmd.data == 0x2) + if (cmd.data == WILC_SDIO_CCCR_IO_EN_FUNC1) break; } while (loop--); @@ -671,8 +674,8 @@ static int wilc_sdio_init(struct wilc *wilc, bool resume) cmd.read_write = 1; cmd.function = 0; cmd.raw = 1; - cmd.address = 0x4; - cmd.data = 0x3; + cmd.address = SDIO_CCCR_IENx; + cmd.data = WILC_SDIO_CCCR_IEN_MASTER | WILC_SDIO_CCCR_IEN_FUNC1; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, "Fail cmd 52, set IEN register...\n"); @@ -711,12 +714,12 @@ static int wilc_sdio_read_size(struct wilc *wilc, u32 *size) cmd.read_write = 0; cmd.function = 0; cmd.raw = 0; - cmd.address = 0xf2; + cmd.address = WILC_SDIO_INTERRUPT_DATA_SZ_REG; cmd.data = 0; wilc_sdio_cmd52(wilc, &cmd); tmp = cmd.data; - cmd.address = 0xf3; + cmd.address = WILC_SDIO_INTERRUPT_DATA_SZ_REG + 1; cmd.data = 0; wilc_sdio_cmd52(wilc, &cmd); tmp |= (cmd.data << 8); @@ -740,10 +743,10 @@ static int wilc_sdio_read_int(struct wilc *wilc, u32 *int_status) **/ if (!sdio_priv->irq_gpio) { cmd.function = 1; - cmd.address = 0x04; + cmd.address = WILC_SDIO_EXT_IRQ_FLAG_REG; } else { cmd.function = 0; - cmd.address = 0xf7; + cmd.address = WILC_SDIO_IRQ_FLAG_REG; } cmd.raw = 0; cmd.read_write = 0; @@ -789,14 +792,14 @@ static int wilc_sdio_clear_int_ext(struct wilc *wilc, u32 val) cmd.read_write = 1; cmd.function = 0; cmd.raw = 0; - cmd.address = 0xf8; + cmd.address = WILC_SDIO_IRQ_CLEAR_FLAG_REG; cmd.data = reg; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, - "Failed cmd52, set 0xf8 data (%d) ...\n", - __LINE__); + "Failed cmd52, set (%02x) data (%d) ...\n", + cmd.address, __LINE__); return ret; } } @@ -819,14 +822,14 @@ static int wilc_sdio_clear_int_ext(struct wilc *wilc, u32 val) cmd.read_write = 1; cmd.function = 0; cmd.raw = 0; - cmd.address = 0xf8; + cmd.address = WILC_SDIO_IRQ_CLEAR_FLAG_REG; cmd.data = BIT(i); ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, - "Failed cmd52, set 0xf8 data (%d) ...\n", - __LINE__); + "Failed cmd52, set (%02x) data (%d) ...\n", + cmd.address, __LINE__); return ret; } flags &= ~BIT(i); @@ -860,13 +863,13 @@ static int wilc_sdio_clear_int_ext(struct wilc *wilc, u32 val) cmd.read_write = 1; cmd.function = 0; cmd.raw = 0; - cmd.address = 0xf6; + cmd.address = WILC_SDIO_VMM_TBL_CTRL_REG; cmd.data = vmm_ctl; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, - "Failed cmd52, set 0xf6 data (%d) ...\n", - __LINE__); + "Failed cmd52, set (%02x) data (%d) ...\n", + cmd.address, __LINE__); return ret; } } diff --git a/drivers/staging/wilc1000/wlan.h b/drivers/staging/wilc1000/wlan.h index 27f08b375345..ceec4801f18f 100644 --- a/drivers/staging/wilc1000/wlan.h +++ b/drivers/staging/wilc1000/wlan.h @@ -93,6 +93,42 @@ #define WILC_SPI_PROTOCOL_OFFSET (WILC_SPI_PROTOCOL_CONFIG - \ WILC_SPI_REG_BASE) +/* Functions IO enables bits */ +#define WILC_SDIO_CCCR_IO_EN_FUNC1 BIT(1) + +/* Function/Interrupt enables bits */ +#define WILC_SDIO_CCCR_IEN_MASTER BIT(0) +#define WILC_SDIO_CCCR_IEN_FUNC1 BIT(1) + +/* Abort CCCR register bits */ +#define WILC_SDIO_CCCR_ABORT_RESET BIT(3) + +/* Vendor specific CCCR registers */ +#define WILC_SDIO_WAKEUP_REG 0xf0 +#define WILC_SDIO_WAKEUP_BIT BIT(0) + +#define WILC_SDIO_CLK_STATUS_REG 0xf1 +#define WILC_SDIO_CLK_STATUS_BIT BIT(0) + +#define WILC_SDIO_INTERRUPT_DATA_SZ_REG 0xf2 /* Read size (2 bytes) */ + +#define WILC_SDIO_VMM_TBL_CTRL_REG 0xf6 +#define WILC_SDIO_IRQ_FLAG_REG 0xf7 +#define WILC_SDIO_IRQ_CLEAR_FLAG_REG 0xf8 + +#define WILC_SDIO_HOST_TO_FW_REG 0xfa +#define WILC_SDIO_HOST_TO_FW_BIT BIT(0) + +#define WILC_SDIO_FW_TO_HOST_REG 0xfc +#define WILC_SDIO_FW_TO_HOST_BIT BIT(0) + +/* Function 1 specific FBR register */ +#define WILC_SDIO_FBR_CSA_REG 0x10C /* CSA pointer (3 bytes) */ +#define WILC_SDIO_FBR_DATA_REG 0x10F + +#define WILC_SDIO_F1_DATA_REG 0x0 +#define WILC_SDIO_EXT_IRQ_FLAG_REG 0x4 + #define WILC_AHB_DATA_MEM_BASE 0x30000 #define WILC_AHB_SHARE_MEM_BASE 0xd0000 From 5832cbe21c16f8a443d4dc0fbe34c1ba3e61093c Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 14 Feb 2020 11:52:22 +0000 Subject: [PATCH 0246/2231] staging: wilc1000: use short name for hif local variable in chip_wakeup() Make use of a shorter name for 'hif' variable to avoid possible 80 character checkpatch warning while the replacing hardcoded value with macros in chip_wakeup(). Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200214172250.13026-8-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/wlan.c | 35 +++++++++++++++------------------ 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/staging/wilc1000/wlan.c b/drivers/staging/wilc1000/wlan.c index db4ef175ccee..31f424efe01d 100644 --- a/drivers/staging/wilc1000/wlan.c +++ b/drivers/staging/wilc1000/wlan.c @@ -403,37 +403,34 @@ EXPORT_SYMBOL_GPL(chip_allow_sleep); void chip_wakeup(struct wilc *wilc) { u32 reg, clk_status_reg; + const struct wilc_hif_func *h = wilc->hif_func; - if ((wilc->io_type & 0x1) == WILC_HIF_SPI) { + if (wilc->io_type == WILC_HIF_SPI) { do { - wilc->hif_func->hif_read_reg(wilc, 1, ®); - wilc->hif_func->hif_write_reg(wilc, 1, reg | BIT(1)); - wilc->hif_func->hif_write_reg(wilc, 1, reg & ~BIT(1)); + h->hif_read_reg(wilc, 1, ®); + h->hif_write_reg(wilc, 1, reg | BIT(1)); + h->hif_write_reg(wilc, 1, reg & ~BIT(1)); do { usleep_range(2000, 2500); wilc_get_chipid(wilc, true); } while (wilc_get_chipid(wilc, true) == 0); } while (wilc_get_chipid(wilc, true) == 0); - } else if ((wilc->io_type & 0x1) == WILC_HIF_SDIO) { - wilc->hif_func->hif_write_reg(wilc, 0xfa, 1); + } else if (wilc->io_type == WILC_HIF_SDIO) { + h->hif_write_reg(wilc, 0xfa, 1); usleep_range(200, 400); - wilc->hif_func->hif_read_reg(wilc, 0xf0, ®); + h->hif_read_reg(wilc, 0xf0, ®); do { - wilc->hif_func->hif_write_reg(wilc, 0xf0, - reg | BIT(0)); - wilc->hif_func->hif_read_reg(wilc, 0xf1, - &clk_status_reg); + h->hif_write_reg(wilc, 0xf0, reg | BIT(0)); + h->hif_read_reg(wilc, 0xf1, &clk_status_reg); while ((clk_status_reg & 0x1) == 0) { usleep_range(2000, 2500); - wilc->hif_func->hif_read_reg(wilc, 0xf1, - &clk_status_reg); + h->hif_read_reg(wilc, 0xf1, &clk_status_reg); } if ((clk_status_reg & 0x1) == 0) { - wilc->hif_func->hif_write_reg(wilc, 0xf0, - reg & (~BIT(0))); + h->hif_write_reg(wilc, 0xf0, reg & (~BIT(0))); } } while ((clk_status_reg & 0x1) == 0); } @@ -442,13 +439,13 @@ void chip_wakeup(struct wilc *wilc) if (wilc_get_chipid(wilc, false) < 0x1002b0) { u32 val32; - wilc->hif_func->hif_read_reg(wilc, 0x1e1c, &val32); + h->hif_read_reg(wilc, 0x1e1c, &val32); val32 |= BIT(6); - wilc->hif_func->hif_write_reg(wilc, 0x1e1c, val32); + h->hif_write_reg(wilc, 0x1e1c, val32); - wilc->hif_func->hif_read_reg(wilc, 0x1e9c, &val32); + h->hif_read_reg(wilc, 0x1e9c, &val32); val32 |= BIT(6); - wilc->hif_func->hif_write_reg(wilc, 0x1e9c, val32); + h->hif_write_reg(wilc, 0x1e9c, val32); } } wilc->chip_ps_state = WILC_CHIP_WAKEDUP; From b1f17561541c09f775b8a23c3e39d58e56beaff4 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 14 Feb 2020 11:52:23 +0000 Subject: [PATCH 0247/2231] staging: wilc1000: define macros to replace magic number values Define macros for global as well as SPI/SDIO specific register to avoid use of magic numbers. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200214172250.13026-9-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/sdio.c | 7 ++- drivers/staging/wilc1000/spi.c | 17 ++++--- drivers/staging/wilc1000/wlan.c | 83 +++++++++++++++++++-------------- drivers/staging/wilc1000/wlan.h | 35 ++++++++++++++ 4 files changed, 99 insertions(+), 43 deletions(-) diff --git a/drivers/staging/wilc1000/sdio.c b/drivers/staging/wilc1000/sdio.c index 0670c5956aef..2301e90c21ca 100644 --- a/drivers/staging/wilc1000/sdio.c +++ b/drivers/staging/wilc1000/sdio.c @@ -686,13 +686,16 @@ static int wilc_sdio_init(struct wilc *wilc, bool resume) * make sure can read back chip id correctly **/ if (!resume) { - ret = wilc_sdio_read_reg(wilc, 0x1000, &chipid); + int rev; + + ret = wilc_sdio_read_reg(wilc, WILC_CHIPID, &chipid); if (ret) { dev_err(&func->dev, "Fail cmd read chip id...\n"); return ret; } dev_err(&func->dev, "chipid (%08x)\n", chipid); - if ((chipid & 0xfff) > 0x2a0) + rev = FIELD_GET(WILC_CHIP_REV_FIELD, chipid); + if (rev > FIELD_GET(WILC_CHIP_REV_FIELD, WILC_1000_BASE_ID_2A)) sdio_priv->has_thrpt_enh3 = 1; else sdio_priv->has_thrpt_enh3 = 0; diff --git a/drivers/staging/wilc1000/spi.c b/drivers/staging/wilc1000/spi.c index 37be627e72ce..44f7d48851b5 100644 --- a/drivers/staging/wilc1000/spi.c +++ b/drivers/staging/wilc1000/spi.c @@ -683,7 +683,7 @@ static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data) u8 cmd = CMD_SINGLE_READ; u8 clockless = 0; - if (addr < 0x30) { + if (addr < WILC_SPI_CLOCKLESS_ADDR_LIMIT) { /* Clockless register */ cmd = CMD_INTERNAL_READ; clockless = 1; @@ -760,7 +760,7 @@ static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data) u8 cmd = CMD_SINGLE_WRITE; u8 clockless = 0; - if (addr < 0x30) { + if (addr < WILC_SPI_CLOCKLESS_ADDR_LIMIT) { /* Clockless register */ cmd = CMD_INTERNAL_WRITE; clockless = 1; @@ -829,7 +829,7 @@ static int wilc_spi_init(struct wilc *wilc, bool resume) int ret; if (isinit) { - ret = wilc_spi_read_reg(wilc, 0x1000, &chipid); + ret = wilc_spi_read_reg(wilc, WILC_CHIPID, &chipid); if (ret) dev_err(&spi->dev, "Fail cmd read chip id...\n"); @@ -881,7 +881,7 @@ static int wilc_spi_init(struct wilc *wilc, bool resume) /* * make sure can read back chip id correctly */ - ret = wilc_spi_read_reg(wilc, 0x1000, &chipid); + ret = wilc_spi_read_reg(wilc, WILC_CHIPID, &chipid); if (ret) { dev_err(&spi->dev, "Fail cmd read chip id...\n"); return ret; @@ -896,7 +896,8 @@ static int wilc_spi_read_size(struct wilc *wilc, u32 *size) { int ret; - ret = spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE, size); + ret = spi_internal_read(wilc, + WILC_SPI_INT_STATUS - WILC_SPI_REG_BASE, size); *size = FIELD_GET(IRQ_DMA_WD_CNT_MASK, *size); return ret; @@ -904,12 +905,14 @@ static int wilc_spi_read_size(struct wilc *wilc, u32 *size) static int wilc_spi_read_int(struct wilc *wilc, u32 *int_status) { - return spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE, int_status); + return spi_internal_read(wilc, WILC_SPI_INT_STATUS - WILC_SPI_REG_BASE, + int_status); } static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val) { - return spi_internal_write(wilc, 0xe844 - WILC_SPI_REG_BASE, val); + return spi_internal_write(wilc, WILC_SPI_INT_CLEAR - WILC_SPI_REG_BASE, + val); } static int wilc_spi_sync_ext(struct wilc *wilc, int nint) diff --git a/drivers/staging/wilc1000/wlan.c b/drivers/staging/wilc1000/wlan.c index 31f424efe01d..f633c6b9f0a9 100644 --- a/drivers/staging/wilc1000/wlan.c +++ b/drivers/staging/wilc1000/wlan.c @@ -11,7 +11,7 @@ static inline bool is_wilc1000(u32 id) { - return (id & 0xfffff000) == 0x100000; + return (id & (~WILC_CHIP_REV_FIELD)) == WILC_1000_BASE_ID; } static inline void acquire_bus(struct wilc *wilc, enum bus_acquire acquire) @@ -393,10 +393,11 @@ void chip_allow_sleep(struct wilc *wilc) { u32 reg = 0; - wilc->hif_func->hif_read_reg(wilc, 0xf0, ®); + wilc->hif_func->hif_read_reg(wilc, WILC_SDIO_WAKEUP_REG, ®); - wilc->hif_func->hif_write_reg(wilc, 0xf0, reg & ~BIT(0)); - wilc->hif_func->hif_write_reg(wilc, 0xfa, 0); + wilc->hif_func->hif_write_reg(wilc, WILC_SDIO_WAKEUP_REG, + reg & ~WILC_SDIO_WAKEUP_BIT); + wilc->hif_func->hif_write_reg(wilc, WILC_SDIO_HOST_TO_FW_REG, 0); } EXPORT_SYMBOL_GPL(chip_allow_sleep); @@ -407,9 +408,11 @@ void chip_wakeup(struct wilc *wilc) if (wilc->io_type == WILC_HIF_SPI) { do { - h->hif_read_reg(wilc, 1, ®); - h->hif_write_reg(wilc, 1, reg | BIT(1)); - h->hif_write_reg(wilc, 1, reg & ~BIT(1)); + h->hif_read_reg(wilc, WILC_SPI_WAKEUP_REG, ®); + h->hif_write_reg(wilc, WILC_SPI_WAKEUP_REG, + reg | WILC_SPI_WAKEUP_BIT); + h->hif_write_reg(wilc, WILC_SPI_WAKEUP_REG, + reg & ~WILC_SPI_WAKEUP_BIT); do { usleep_range(2000, 2500); @@ -417,35 +420,40 @@ void chip_wakeup(struct wilc *wilc) } while (wilc_get_chipid(wilc, true) == 0); } while (wilc_get_chipid(wilc, true) == 0); } else if (wilc->io_type == WILC_HIF_SDIO) { - h->hif_write_reg(wilc, 0xfa, 1); + h->hif_write_reg(wilc, WILC_SDIO_HOST_TO_FW_REG, + WILC_SDIO_HOST_TO_FW_BIT); usleep_range(200, 400); - h->hif_read_reg(wilc, 0xf0, ®); + h->hif_read_reg(wilc, WILC_SDIO_WAKEUP_REG, ®); do { - h->hif_write_reg(wilc, 0xf0, reg | BIT(0)); - h->hif_read_reg(wilc, 0xf1, &clk_status_reg); + h->hif_write_reg(wilc, WILC_SDIO_WAKEUP_REG, + reg | WILC_SDIO_WAKEUP_BIT); + h->hif_read_reg(wilc, WILC_SDIO_CLK_STATUS_REG, + &clk_status_reg); - while ((clk_status_reg & 0x1) == 0) { + while (!(clk_status_reg & WILC_SDIO_CLK_STATUS_BIT)) { usleep_range(2000, 2500); - h->hif_read_reg(wilc, 0xf1, &clk_status_reg); + h->hif_read_reg(wilc, WILC_SDIO_CLK_STATUS_REG, + &clk_status_reg); } - if ((clk_status_reg & 0x1) == 0) { - h->hif_write_reg(wilc, 0xf0, reg & (~BIT(0))); + if (!(clk_status_reg & WILC_SDIO_CLK_STATUS_BIT)) { + h->hif_write_reg(wilc, WILC_SDIO_WAKEUP_REG, + reg & ~WILC_SDIO_WAKEUP_BIT); } - } while ((clk_status_reg & 0x1) == 0); + } while (!(clk_status_reg & WILC_SDIO_CLK_STATUS_BIT)); } if (wilc->chip_ps_state == WILC_CHIP_SLEEPING_MANUAL) { - if (wilc_get_chipid(wilc, false) < 0x1002b0) { + if (wilc_get_chipid(wilc, false) < WILC_1000_BASE_ID_2B) { u32 val32; - h->hif_read_reg(wilc, 0x1e1c, &val32); + h->hif_read_reg(wilc, WILC_REG_4_TO_1_RX, &val32); val32 |= BIT(6); - h->hif_write_reg(wilc, 0x1e1c, val32); + h->hif_write_reg(wilc, WILC_REG_4_TO_1_RX, val32); - h->hif_read_reg(wilc, 0x1e9c, &val32); + h->hif_read_reg(wilc, WILC_REG_4_TO_1_TX_BANK0, &val32); val32 |= BIT(6); - h->hif_write_reg(wilc, 0x1e9c, val32); + h->hif_write_reg(wilc, WILC_REG_4_TO_1_TX_BANK0, val32); } } wilc->chip_ps_state = WILC_CHIP_WAKEDUP; @@ -455,7 +463,7 @@ EXPORT_SYMBOL_GPL(chip_wakeup); void host_wakeup_notify(struct wilc *wilc) { acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY); - wilc->hif_func->hif_write_reg(wilc, 0x10b0, 1); + wilc->hif_func->hif_write_reg(wilc, WILC_CORTUS_INTERRUPT_2, 1); release_bus(wilc, WILC_BUS_RELEASE_ONLY); } EXPORT_SYMBOL_GPL(host_wakeup_notify); @@ -463,7 +471,7 @@ EXPORT_SYMBOL_GPL(host_wakeup_notify); void host_sleep_notify(struct wilc *wilc) { acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY); - wilc->hif_func->hif_write_reg(wilc, 0x10ac, 1); + wilc->hif_func->hif_write_reg(wilc, WILC_CORTUS_INTERRUPT_1, 1); release_bus(wilc, WILC_BUS_RELEASE_ONLY); } EXPORT_SYMBOL_GPL(host_sleep_notify); @@ -884,7 +892,7 @@ int wilc_wlan_start(struct wilc *wilc) wilc->hif_func->hif_sync_ext(wilc, NUM_INT_EXT); - ret = wilc->hif_func->hif_read_reg(wilc, 0x1000, &chipid); + ret = wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &chipid); if (ret) { release_bus(wilc, WILC_BUS_RELEASE_ONLY); return ret; @@ -1125,18 +1133,24 @@ static int init_chip(struct net_device *dev) chipid = wilc_get_chipid(wilc, true); if ((chipid & 0xfff) != 0xa0) { - ret = wilc->hif_func->hif_read_reg(wilc, 0x1118, ®); + ret = wilc->hif_func->hif_read_reg(wilc, + WILC_CORTUS_RESET_MUX_SEL, + ®); if (ret) { netdev_err(dev, "fail read reg 0x1118\n"); goto release; } reg |= BIT(0); - ret = wilc->hif_func->hif_write_reg(wilc, 0x1118, reg); + ret = wilc->hif_func->hif_write_reg(wilc, + WILC_CORTUS_RESET_MUX_SEL, + reg); if (ret) { netdev_err(dev, "fail write reg 0x1118\n"); goto release; } - ret = wilc->hif_func->hif_write_reg(wilc, 0xc0000, 0x71); + ret = wilc->hif_func->hif_write_reg(wilc, + WILC_CORTUS_BOOT_REGISTER, + WILC_CORTUS_BOOT_FROM_IRAM); if (ret) { netdev_err(dev, "fail write reg 0xc0000\n"); goto release; @@ -1156,20 +1170,21 @@ u32 wilc_get_chipid(struct wilc *wilc, bool update) u32 rfrevid = 0; if (chipid == 0 || update) { - wilc->hif_func->hif_read_reg(wilc, 0x1000, &tempchipid); - wilc->hif_func->hif_read_reg(wilc, 0x13f4, &rfrevid); + wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &tempchipid); + wilc->hif_func->hif_read_reg(wilc, WILC_RF_REVISION_ID, + &rfrevid); if (!is_wilc1000(tempchipid)) { chipid = 0; return chipid; } - if (tempchipid == 0x1002a0) { + if (tempchipid == WILC_1000_BASE_ID_2A) { /* 0x1002A0 */ if (rfrevid != 0x1) - tempchipid = 0x1002a1; - } else if (tempchipid == 0x1002b0) { + tempchipid = WILC_1000_BASE_ID_2A_REV1; + } else if (tempchipid == WILC_1000_BASE_ID_2B) { /* 0x1002B0 */ if (rfrevid == 0x4) - tempchipid = 0x1002b1; + tempchipid = WILC_1000_BASE_ID_2B_REV1; else if (rfrevid != 0x3) - tempchipid = 0x1002b2; + tempchipid = WILC_1000_BASE_ID_2B_REV2; } chipid = tempchipid; diff --git a/drivers/staging/wilc1000/wlan.h b/drivers/staging/wilc1000/wlan.h index ceec4801f18f..5999c5490ea5 100644 --- a/drivers/staging/wilc1000/wlan.h +++ b/drivers/staging/wilc1000/wlan.h @@ -66,6 +66,8 @@ #define WILC_INTR_CLEAR (WILC_INTR_REG_BASE + 0x30) #define WILC_INTR_STATUS (WILC_INTR_REG_BASE + 0x40) +#define WILC_RF_REVISION_ID 0x13f4 + #define WILC_VMM_TBL_SIZE 64 #define WILC_VMM_TX_TBL_BASE 0x150400 #define WILC_VMM_RX_TBL_BASE 0x150500 @@ -89,10 +91,17 @@ #define WILC_SPI_TX_MODE (WILC_SPI_REG_BASE + 0x20) #define WILC_SPI_PROTOCOL_CONFIG (WILC_SPI_REG_BASE + 0x24) #define WILC_SPI_INTR_CTL (WILC_SPI_REG_BASE + 0x2c) +#define WILC_SPI_INT_STATUS (WILC_SPI_REG_BASE + 0x40) +#define WILC_SPI_INT_CLEAR (WILC_SPI_REG_BASE + 0x44) + +#define WILC_SPI_WAKEUP_REG 0x1 +#define WILC_SPI_WAKEUP_BIT BIT(1) #define WILC_SPI_PROTOCOL_OFFSET (WILC_SPI_PROTOCOL_CONFIG - \ WILC_SPI_REG_BASE) +#define WILC_SPI_CLOCKLESS_ADDR_LIMIT 0x30 + /* Functions IO enables bits */ #define WILC_SDIO_CCCR_IO_EN_FUNC1 BIT(1) @@ -149,6 +158,32 @@ #define WILC_HAVE_DISABLE_WILC_UART BIT(7) #define WILC_HAVE_USE_IRQ_AS_HOST_WAKE BIT(8) +#define WILC_CORTUS_INTERRUPT_BASE 0x10A8 +#define WILC_CORTUS_INTERRUPT_1 (WILC_CORTUS_INTERRUPT_BASE + 0x4) +#define WILC_CORTUS_INTERRUPT_2 (WILC_CORTUS_INTERRUPT_BASE + 0x8) + +/* tx control register 1 to 4 for RX */ +#define WILC_REG_4_TO_1_RX 0x1e1c + +/* tx control register 1 to 4 for TX Bank_0 */ +#define WILC_REG_4_TO_1_TX_BANK0 0x1e9c + +#define WILC_CORTUS_RESET_MUX_SEL 0x1118 +#define WILC_CORTUS_BOOT_REGISTER 0xc0000 + +#define WILC_CORTUS_BOOT_FROM_IRAM 0x71 + +#define WILC_1000_BASE_ID 0x100000 + +#define WILC_1000_BASE_ID_2A 0x1002A0 +#define WILC_1000_BASE_ID_2A_REV1 (WILC_1000_BASE_ID_2A + 1) + +#define WILC_1000_BASE_ID_2B 0x1002B0 +#define WILC_1000_BASE_ID_2B_REV1 (WILC_1000_BASE_ID_2B + 1) +#define WILC_1000_BASE_ID_2B_REV2 (WILC_1000_BASE_ID_2B + 2) + +#define WILC_CHIP_REV_FIELD GENMASK(11, 0) + /******************************************** * * Wlan Defines From 0616ca73fd35409b0d8d2a17bbca42f6febcd235 Mon Sep 17 00:00:00 2001 From: chenqiwu Date: Fri, 14 Feb 2020 20:37:00 +0800 Subject: [PATCH 0248/2231] usb: use kobj_to_dev() API Use kobj_to_dev() API instead of container_of(). Signed-off-by: chenqiwu Link: https://lore.kernel.org/r/1581683820-9978-1-git-send-email-qiwuchen55@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/sysfs.c | 6 +++--- drivers/usb/roles/class.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index f19694e69f5c..9f4320b9d7fc 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -849,7 +849,7 @@ static struct attribute *dev_string_attrs[] = { static umode_t dev_string_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct usb_device *udev = to_usb_device(dev); if (a == &dev_attr_manufacturer.attr) { @@ -883,7 +883,7 @@ read_descriptors(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct usb_device *udev = to_usb_device(dev); size_t nleft = count; size_t srclen, n; @@ -1233,7 +1233,7 @@ static struct attribute *intf_assoc_attrs[] = { static umode_t intf_assoc_attrs_are_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct usb_interface *intf = to_usb_interface(dev); if (intf->intf_assoc == NULL) diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 63a00ff26655..486b0b1e2a7a 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -199,7 +199,7 @@ EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode); static umode_t usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = container_of(kobj, typeof(*dev), kobj); + struct device *dev = kobj_to_dev(kobj); struct usb_role_switch *sw = to_role_switch(dev); if (sw->allow_userspace_control) From 21d78d860cd604330dfc373f001a48e548d2c49e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 14 Feb 2020 15:16:38 +0300 Subject: [PATCH 0249/2231] thunderbolt: Add missing kernel-doc parameter descriptions Two functions that were added for USB4 support miss kernel-doc parameter descriptions so add them now. Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20200214121638.75589-1-mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/usb4.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index b341fc60c4ba..3d084cec136f 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -259,6 +259,7 @@ int usb4_switch_setup(struct tb_switch *sw) /** * usb4_switch_read_uid() - Read UID from USB4 router * @sw: USB4 router + * @uid: UID is stored here * * Reads 64-bit UID from USB4 router config space. */ @@ -296,6 +297,9 @@ static int usb4_switch_drom_read_block(struct tb_switch *sw, /** * usb4_switch_drom_read() - Read arbitrary bytes from USB4 router DROM * @sw: USB4 router + * @address: Byte address inside DROM to start reading + * @buf: Buffer where the DROM content is stored + * @size: Number of bytes to read from DROM * * Uses USB4 router operations to read router DROM. For devices this * should always work but for hosts it may return %-EOPNOTSUPP in which From 6ecc632d4b35d24c443d3c3b797aa204cc5c4ab1 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Fri, 14 Feb 2020 15:53:48 +0800 Subject: [PATCH 0250/2231] usb: typec: tcpm: set correct data role for non-DRD Since the typec port data role is separated from power role, so check the port data capability when setting data role. Signed-off-by: Li Jun Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/1581666828-2063-1-git-send-email-jun.li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 53 +++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index f3087ef8265c..78077c234ef2 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -373,6 +373,14 @@ struct pd_rx_event { ((port)->try_src_count == 0 && (port)->try_role == TYPEC_SOURCE && \ (port)->port_type == TYPEC_PORT_DRP) +#define tcpm_data_role_for_source(port) \ + ((port)->typec_caps.data == TYPEC_PORT_UFP ? \ + TYPEC_DEVICE : TYPEC_HOST) + +#define tcpm_data_role_for_sink(port) \ + ((port)->typec_caps.data == TYPEC_PORT_DFP ? \ + TYPEC_HOST : TYPEC_DEVICE) + static enum tcpm_state tcpm_default_state(struct tcpm_port *port) { if (port->port_type == TYPEC_PORT_DRP) { @@ -788,10 +796,30 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, else orientation = TYPEC_ORIENTATION_REVERSE; - if (data == TYPEC_HOST) - usb_role = USB_ROLE_HOST; - else - usb_role = USB_ROLE_DEVICE; + if (port->typec_caps.data == TYPEC_PORT_DRD) { + if (data == TYPEC_HOST) + usb_role = USB_ROLE_HOST; + else + usb_role = USB_ROLE_DEVICE; + } else if (port->typec_caps.data == TYPEC_PORT_DFP) { + if (data == TYPEC_HOST) { + if (role == TYPEC_SOURCE) + usb_role = USB_ROLE_HOST; + else + usb_role = USB_ROLE_NONE; + } else { + return -ENOTSUPP; + } + } else { + if (data == TYPEC_DEVICE) { + if (role == TYPEC_SINK) + usb_role = USB_ROLE_DEVICE; + else + usb_role = USB_ROLE_NONE; + } else { + return -ENOTSUPP; + } + } ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation); if (ret < 0) @@ -1817,7 +1845,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, tcpm_set_state(port, SOFT_RESET, 0); break; case PD_CTRL_DR_SWAP: - if (port->port_type != TYPEC_PORT_DRP) { + if (port->typec_caps.data != TYPEC_PORT_DRD) { tcpm_queue_message(port, PD_MSG_CTRL_REJECT); break; } @@ -2618,7 +2646,8 @@ static int tcpm_src_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST); + ret = tcpm_set_roles(port, true, TYPEC_SOURCE, + tcpm_data_role_for_source(port)); if (ret < 0) return ret; @@ -2740,7 +2769,8 @@ static int tcpm_snk_attach(struct tcpm_port *port) if (ret < 0) return ret; - ret = tcpm_set_roles(port, true, TYPEC_SINK, TYPEC_DEVICE); + ret = tcpm_set_roles(port, true, TYPEC_SINK, + tcpm_data_role_for_sink(port)); if (ret < 0) return ret; @@ -2766,7 +2796,8 @@ static int tcpm_acc_attach(struct tcpm_port *port) if (port->attached) return 0; - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST); + ret = tcpm_set_roles(port, true, TYPEC_SOURCE, + tcpm_data_role_for_source(port)); if (ret < 0) return ret; @@ -3293,7 +3324,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_vconn(port, true); tcpm_set_vbus(port, false); tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE, - TYPEC_HOST); + tcpm_data_role_for_source(port)); tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); break; case SRC_HARD_RESET_VBUS_ON: @@ -3308,7 +3339,7 @@ static void run_state_machine(struct tcpm_port *port) if (port->pd_capable) tcpm_set_charge(port, false); tcpm_set_roles(port, port->self_powered, TYPEC_SINK, - TYPEC_DEVICE); + tcpm_data_role_for_sink(port)); /* * VBUS may or may not toggle, depending on the adapter. * If it doesn't toggle, transition to SNK_HARD_RESET_SINK_ON @@ -3969,7 +4000,7 @@ static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data) mutex_lock(&port->swap_lock); mutex_lock(&port->lock); - if (port->port_type != TYPEC_PORT_DRP) { + if (port->typec_caps.data != TYPEC_PORT_DRD) { ret = -EINVAL; goto port_unlock; } From 21a37aeda674c0f92d071f5a64ac87d8f3d9305d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 17:25:19 -0600 Subject: [PATCH 0251/2231] USB: musb: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200211232519.GA23263@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 886c9b602f8c..1c813c37462a 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2550,7 +2550,7 @@ static int musb_bus_resume(struct usb_hcd *hcd) struct musb_temp_buffer { void *kmalloc_ptr; void *old_xfer_buffer; - u8 data[0]; + u8 data[]; }; static void musb_free_temp_buffer(struct urb *urb) From b57a368efde6b2980606169cdcaa1c4101adffe0 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 17:23:03 -0600 Subject: [PATCH 0252/2231] usb: gadget: f_phonet: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200211232303.GA21495@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_phonet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index 8b72b192c747..d7f6cc51b7ec 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -48,7 +48,7 @@ struct f_phonet { struct usb_ep *in_ep, *out_ep; struct usb_request *in_req; - struct usb_request *out_reqv[0]; + struct usb_request *out_reqv[]; }; static int phonet_rxq_size = 17; From e92a4eb490cb445fd644cc9a344db3f01b866d29 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 11 Feb 2020 19:15:14 +0100 Subject: [PATCH 0253/2231] drivers base/arch_topology: Remove 'struct sched_domain' forward declaration The sched domain pointer argument from topology_get_freq_scale() and topology_get_cpu_scale() got removed by commit 7673c8a4c75d ("sched/cpufreq: Remove arch_scale_freq_capacity()'s 'sd' parameter") and commit 8ec59c0f5f49 ("sched/topology: Remove unused 'sd' parameter from arch_scale_cpu_capacity()"). So the 'struct sched_domain' forward declaration is no longer needed. Remove it. Signed-off-by: Dietmar Eggemann Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20200211181515.32570-2-dietmar.eggemann@arm.com Signed-off-by: Greg Kroah-Hartman --- include/linux/arch_topology.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 3015ecbb90b1..f4b1d4fd7efb 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -16,7 +16,6 @@ bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); DECLARE_PER_CPU(unsigned long, cpu_scale); -struct sched_domain; static inline unsigned long topology_get_cpu_scale(int cpu) { From 99c73ce158a44a1a11cb146062bc32df0c1d95db Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 11 Feb 2020 19:15:15 +0100 Subject: [PATCH 0254/2231] drivers base/arch_topology: Reformat topology_get_[cpu/freq]_scale() function name The storage class and inline definition as well as the return type, function name and parameter list fit all into one line. Signed-off-by: Dietmar Eggemann Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20200211181515.32570-3-dietmar.eggemann@arm.com Signed-off-by: Greg Kroah-Hartman --- include/linux/arch_topology.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index f4b1d4fd7efb..c507e9ddd909 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -16,8 +16,7 @@ bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); DECLARE_PER_CPU(unsigned long, cpu_scale); -static inline -unsigned long topology_get_cpu_scale(int cpu) +static inline unsigned long topology_get_cpu_scale(int cpu) { return per_cpu(cpu_scale, cpu); } @@ -26,8 +25,7 @@ void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity); DECLARE_PER_CPU(unsigned long, freq_scale); -static inline -unsigned long topology_get_freq_scale(int cpu) +static inline unsigned long topology_get_freq_scale(int cpu) { return per_cpu(freq_scale, cpu); } From 1f57e6a2c4ae3840449ea1c2708cea43aa15f3ec Mon Sep 17 00:00:00 2001 From: Erik Kaneda Date: Fri, 14 Feb 2020 10:47:55 -0800 Subject: [PATCH 0255/2231] ACPICA: ASL-ASL+ converter: remove function parameters from cv_init_file_tree() ACPICA commit 3ba6fa55a4a32d8b6fc28f9f285506ea0e359296 These parameters can be computed inside of the function from the Table parameter. Link: https://github.com/acpica/acpica/commit/3ba6fa55 Signed-off-by: Erik Kaneda Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acconvert.h | 4 +--- drivers/acpi/acpica/acmacros.h | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/acpica/acconvert.h b/drivers/acpi/acpica/acconvert.h index ede4b9cc9e85..9eca93656116 100644 --- a/drivers/acpi/acpica/acconvert.h +++ b/drivers/acpi/acpica/acconvert.h @@ -65,9 +65,7 @@ void cg_write_aml_comment(union acpi_parse_object *op); /* * cvparser */ -void -cv_init_file_tree(struct acpi_table_header *table, - u8 *aml_start, u32 aml_length); +void cv_init_file_tree(struct acpi_table_header *table); void cv_clear_op_comments(union acpi_parse_object *op); diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 2269e10bc21b..87d06c963a60 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -477,7 +477,7 @@ #define ASL_CV_PRINT_ONE_COMMENT(a,b,c,d) cv_print_one_comment_type (a,b,c,d); #define ASL_CV_PRINT_ONE_COMMENT_LIST(a,b) cv_print_one_comment_list (a,b); #define ASL_CV_FILE_HAS_SWITCHED(a) cv_file_has_switched(a) -#define ASL_CV_INIT_FILETREE(a,b,c) cv_init_file_tree(a,b,c); +#define ASL_CV_INIT_FILETREE(a) cv_init_file_tree(a); #else @@ -492,7 +492,7 @@ #define ASL_CV_PRINT_ONE_COMMENT(a,b,c,d) #define ASL_CV_PRINT_ONE_COMMENT_LIST(a,b) #define ASL_CV_FILE_HAS_SWITCHED(a) 0 -#define ASL_CV_INIT_FILETREE(a,b,c) +#define ASL_CV_INIT_FILETREE(a) #endif From 9e43f5d544e40d64686a38372d41d8100401d28c Mon Sep 17 00:00:00 2001 From: Erik Kaneda Date: Fri, 14 Feb 2020 10:47:56 -0800 Subject: [PATCH 0256/2231] ACPICA: ASL-ASL+ converter: make root file a parameter for cv_init_file_tree ACPICA commit 5d160cc86cca440eac7055f981e48bc14d4eb9f7 This decouples cv_init_file_tree from acpi_gbl_output_file and allows it to be called independently of acpi_os_redirect_output() Link: https://github.com/acpica/acpica/commit/5d160cc8 Signed-off-by: Erik Kaneda Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acconvert.h | 2 +- drivers/acpi/acpica/acmacros.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpica/acconvert.h b/drivers/acpi/acpica/acconvert.h index 9eca93656116..cf85d66da6e7 100644 --- a/drivers/acpi/acpica/acconvert.h +++ b/drivers/acpi/acpica/acconvert.h @@ -65,7 +65,7 @@ void cg_write_aml_comment(union acpi_parse_object *op); /* * cvparser */ -void cv_init_file_tree(struct acpi_table_header *table); +void cv_init_file_tree(struct acpi_table_header *table, FILE * root_file); void cv_clear_op_comments(union acpi_parse_object *op); diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 87d06c963a60..168904ba3086 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -477,7 +477,7 @@ #define ASL_CV_PRINT_ONE_COMMENT(a,b,c,d) cv_print_one_comment_type (a,b,c,d); #define ASL_CV_PRINT_ONE_COMMENT_LIST(a,b) cv_print_one_comment_list (a,b); #define ASL_CV_FILE_HAS_SWITCHED(a) cv_file_has_switched(a) -#define ASL_CV_INIT_FILETREE(a) cv_init_file_tree(a); +#define ASL_CV_INIT_FILETREE(a,b) cv_init_file_tree(a,b); #else @@ -492,7 +492,7 @@ #define ASL_CV_PRINT_ONE_COMMENT(a,b,c,d) #define ASL_CV_PRINT_ONE_COMMENT_LIST(a,b) #define ASL_CV_FILE_HAS_SWITCHED(a) 0 -#define ASL_CV_INIT_FILETREE(a) +#define ASL_CV_INIT_FILETREE(a,b) #endif From 4444b178529300a173b927bbef3e65d328fc15be Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 14 Feb 2020 10:47:57 -0800 Subject: [PATCH 0257/2231] ACPICA: Fix a comment "enable" fixed events -> "disable" all fixed events. ACPICA commit af4462c6f41ebc9bf63b5370818c5fd96524e7a9 Link: https://github.com/acpica/acpica/commit/af4462c6 Signed-off-by: Bob Moore Signed-off-by: Erik Kaneda Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/evevent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index 8c83d8c620dc..89391938193d 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -130,7 +130,7 @@ static acpi_status acpi_ev_fixed_event_initialize(void) /* * Initialize the structure that keeps track of fixed event handlers and - * enable the fixed events. + * disable all of the fixed events. */ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { acpi_gbl_fixed_event_handlers[i].handler = NULL; From a1dc35b840e9e7a91663a288ecb4912b9e4a1406 Mon Sep 17 00:00:00 2001 From: Anchal Agarwal Date: Fri, 14 Feb 2020 10:47:58 -0800 Subject: [PATCH 0258/2231] ACPICA: Enable sleep button on ACPI legacy wake ACPICA commit 9383f5b01091e432c05f802a57edc20d329eec1f Hibernation (S4) is triggered in a guest when it recieves a sleep trigger from the hypervisor. When the guest resumes from this power state, it does not see the sleep_enabled bit. In otherwords, the sleep button is not enabled on waking from an S4 state. This causes subsequent invocation of sleep state to fail in the guest. Fix this problem by enabling the sleep button in ACPI legacy wake. Signed-off-by: Anchal Agarwal Reviewed-by: Balbir Singh Reviewed-by: Frank van der Linden [ ek: changelog] Link: https://github.com/acpica/acpica/commit/9383f5b0 Signed-off-by: Erik Kaneda Signed-off-by: Anchal Agarwal Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/hwsleep.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index 243a25add28f..317ae870336b 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -300,6 +300,18 @@ acpi_status acpi_hw_legacy_wake(u8 sleep_state) [ACPI_EVENT_POWER_BUTTON]. status_register_id, ACPI_CLEAR_STATUS); + /* Enable sleep button */ + + (void) + acpi_write_bit_register(acpi_gbl_fixed_event_info + [ACPI_EVENT_SLEEP_BUTTON]. + enable_register_id, ACPI_ENABLE_EVENT); + + (void) + acpi_write_bit_register(acpi_gbl_fixed_event_info + [ACPI_EVENT_SLEEP_BUTTON]. + status_register_id, ACPI_CLEAR_STATUS); + acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING); return_ACPI_STATUS(status); } From 464fa2bd52c190d0b7bde7c2c1e0f0af7a6c33d4 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 14 Feb 2020 10:47:59 -0800 Subject: [PATCH 0259/2231] ACPICA: Table Manager: Update comments in a function header Update acpi_get_table_header to remove text stating that the caller must unmap any returned memory. ACPICA commit 4f3a235cf0044b2d91958b1f99b4ca824c63f948 Link: https://github.com/acpica/acpica/commit/4f3a235c Signed-off-by: Bob Moore Signed-off-by: Erik Kaneda Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/tbxface.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index f8403d480318..7490429ddbf6 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -202,14 +202,14 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_reallocate_root_table) * * PARAMETERS: signature - ACPI signature of needed table * instance - Which instance (for SSDTs) - * out_table_header - The pointer to the table header to fill + * out_table_header - The pointer to the where the table header + * is returned * - * RETURN: Status and pointer to mapped table header + * RETURN: Status and a copy of the table header * - * DESCRIPTION: Finds an ACPI table header. - * - * NOTE: Caller is responsible in unmapping the header with - * acpi_os_unmap_memory + * DESCRIPTION: Finds and returns an ACPI table header. Caller provides the + * memory where a copy of the header is to be returned + * (fixed length). * ******************************************************************************/ acpi_status From 45bbbbe2d5bcfe72754da275298f5de9120a4c31 Mon Sep 17 00:00:00 2001 From: Sven Barth Date: Fri, 14 Feb 2020 10:48:00 -0800 Subject: [PATCH 0260/2231] ACPICA: cast the result of the pointer difference to u32 Altnatively we could declare aml_length as acpi_size, but then one would need to cast at the assignment for method_obj->method.aml_length ACPICA commit 72805936603fcf84e98f1b89bf99b5101af27fb8 Link: https://github.com/acpica/acpica/commit/72805936 Signed-off-by: Sven Barth Signed-off-by: Bob Moore Signed-off-by: Erik Kaneda Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/nsxfname.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index 984129dcaa0c..0e6aba81605b 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -516,7 +516,7 @@ acpi_status acpi_install_method(u8 *buffer) method_flags = *parser_state.aml++; aml_start = parser_state.aml; - aml_length = ACPI_PTR_DIFF(parser_state.pkg_end, aml_start); + aml_length = (u32)ACPI_PTR_DIFF(parser_state.pkg_end, aml_start); /* * Allocate resources up-front. We don't want to have to delete a new From 8dbb75ce331749ff178b73386763e31224b5e047 Mon Sep 17 00:00:00 2001 From: Sven Barth Date: Fri, 14 Feb 2020 10:48:01 -0800 Subject: [PATCH 0261/2231] ACPICA: cast length arguement to acpi_ns_build_normalized_path() as u32 ACPICA commit d216e4c8d886d7fb82697948c4fee8a5777a1a5a Link: https://github.com/acpica/acpica/commit/d216e4c8 Signed-off-by: Sven Barth Signed-off-by: Bob Moore Signed-off-by: Erik Kaneda Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/nsnames.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index 370bbc867745..832b79c4b008 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -164,7 +164,7 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle, /* Build the path in the caller buffer */ (void)acpi_ns_build_normalized_path(node, buffer->pointer, - required_size, no_trailing); + (u32)required_size, no_trailing); ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s [%X]\n", (char *)buffer->pointer, (u32) required_size)); @@ -315,7 +315,7 @@ char *acpi_ns_get_normalized_pathname(struct acpi_namespace_node *node, /* Build the path in the allocated buffer */ - (void)acpi_ns_build_normalized_path(node, name_buffer, size, + (void)acpi_ns_build_normalized_path(node, name_buffer, (u32)size, no_trailing); ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES, "%s: Path \"%s\"\n", From a34991fdf421170ee634c73b5fe0785817dcb11e Mon Sep 17 00:00:00 2001 From: Sven Barth Date: Fri, 14 Feb 2020 10:48:02 -0800 Subject: [PATCH 0262/2231] ACPICA: use acpi_size instead of u32 for prefix_path_length strlen() returns a size_t, so use acpi_size instead of u32 for prefix_path_length. ACPICA commit 0f64c317e769a63679442404421da1d5bd61068a Link: https://github.com/acpica/acpica/commit/0f64c317 Signed-off-by: Sven Barth Signed-off-by: Bob Moore Signed-off-by: Erik Kaneda Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/nsnames.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index 832b79c4b008..d4d26147610e 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -346,7 +346,7 @@ char *acpi_ns_build_prefixed_pathname(union acpi_generic_state *prefix_scope, char *full_path = NULL; char *external_path = NULL; char *prefix_path = NULL; - u32 prefix_path_length = 0; + acpi_size prefix_path_length = 0; /* If there is a prefix, get the pathname to it */ From 54a2a15a56648998ac6ca103f33fe5cdb672ae7a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 14 Feb 2020 10:48:03 -0800 Subject: [PATCH 0263/2231] ACPICA: Fix a couple of typos Squashed ACPICA commit e93a53d4d312a83a3ec72aa9cfb12d781b4fefca and df52c574572344cd9095b66a0f580d51249deb2a Submitted by: ehaouas@noos.fr Link: https://github.com/acpica/acpica/commit/e93a53d4 Signed-off-by: Bob Moore Signed-off-by: Erik Kaneda Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utobject.c | 2 +- include/acpi/actbl1.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index 3e60bdac2200..bbec04c291d2 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -44,7 +44,7 @@ acpi_ut_get_element_length(u8 object_type, * * NOTE: We always allocate the worst-case object descriptor because * these objects are cached, and we want them to be - * one-size-satisifies-any-request. This in itself may not be + * one-size-satisfies-any-request. This in itself may not be * the most memory efficient, but the efficiency of the object * cache should more than make up for this! * diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 02d06b79e1cd..43549547ed3e 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -862,7 +862,7 @@ enum acpi_erst_instructions { /* Command status return values */ enum acpi_erst_command_status { - ACPI_ERST_SUCESS = 0, + ACPI_ERST_SUCCESS = 0, ACPI_ERST_NO_SPACE = 1, ACPI_ERST_NOT_AVAILABLE = 2, ACPI_ERST_FAILURE = 3, From fcec45910e7bcfbf2ef7d31515c38a82f23f6b22 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 14 Feb 2020 15:24:46 +0100 Subject: [PATCH 0264/2231] usb: gadget: amd5536udc: fix spelling mistake "reserverd" -> "reserved" The variable is named reserved, the comment should say so. Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20200214142446.22483-1-alexandre.belloni@bootlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/amd5536udc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index dfdef6a28904..0262383f8c79 100644 --- a/drivers/usb/gadget/udc/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h @@ -440,7 +440,7 @@ struct udc_ep_regs { /* endpoint data descriptor pointer */ u32 desptr; - /* reserverd */ + /* reserved */ u32 reserved; /* write/read confirmation */ From 16105850f7fcf91be28bc9958b9a502406a40926 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 14 Feb 2020 11:56:34 -0500 Subject: [PATCH 0265/2231] xhci: Do not open code __print_symbolic() in xhci trace events libtraceevent (used by perf and trace-cmd) failed to parse the xhci_urb_dequeue trace event. This is because the user space trace event format parsing is not a full C compiler. It can handle some basic logic, but is not meant to be able to handle everything C can do. In cases where a trace event field needs to be converted from a number to a string, there's the __print_symbolic() macro that should be used: See samples/trace_events/trace-events-sample.h Some xhci trace events open coded the __print_symbolic() causing the user spaces tools to fail to parse it. This has to be replaced with __print_symbolic() instead. CC: stable@vger.kernel.org Reported-by: Tzvetomir Stoyanov Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=206531 Fixes: 5abdc2e6e12ff ("usb: host: xhci: add urb_enqueue/dequeue/giveback tracers") Signed-off-by: Steven Rostedt (VMware) Link: https://lore.kernel.org/r/20200214115634.30e8ebf2@gandalf.local.home Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-trace.h | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 56eb867803a6..b19582b2a72c 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -289,23 +289,12 @@ DECLARE_EVENT_CLASS(xhci_log_urb, ), TP_printk("ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x", __entry->epnum, __entry->dir_in ? "in" : "out", - ({ char *s; - switch (__entry->type) { - case USB_ENDPOINT_XFER_INT: - s = "intr"; - break; - case USB_ENDPOINT_XFER_CONTROL: - s = "control"; - break; - case USB_ENDPOINT_XFER_BULK: - s = "bulk"; - break; - case USB_ENDPOINT_XFER_ISOC: - s = "isoc"; - break; - default: - s = "UNKNOWN"; - } s; }), __entry->urb, __entry->pipe, __entry->slot_id, + __print_symbolic(__entry->type, + { USB_ENDPOINT_XFER_INT, "intr" }, + { USB_ENDPOINT_XFER_CONTROL, "control" }, + { USB_ENDPOINT_XFER_BULK, "bulk" }, + { USB_ENDPOINT_XFER_ISOC, "isoc" }), + __entry->urb, __entry->pipe, __entry->slot_id, __entry->actual, __entry->length, __entry->num_mapped_sgs, __entry->num_sgs, __entry->stream, __entry->flags ) From dce174e01d864ebc51091f404f0b1df2504340a5 Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Fri, 14 Feb 2020 20:47:33 +0000 Subject: [PATCH 0266/2231] xhci: Add missing annotation for xhci_set_port_power() Sparse reports a warning at xhci_set_port_power() warning: context imbalance in xhci_set_port_power - unexpected unlock The root cause is the missing annotation at xhci_set_port_power() Add the missing __must_hold(&xhci->lock) annotattion Signed-off-by: Jules Irenge Link: https://lore.kernel.org/r/20200214204741.94112-23-jbi.octave@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 7a3a29e5e9d2..57e3f2c5475a 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -558,6 +558,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) */ static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, u16 index, bool on, unsigned long *flags) + __must_hold(&xhci->lock) { struct xhci_hub *rhub; struct xhci_port *port; From 055b185a3326cf18f5ee27f0ad251e76cccbd6cf Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Fri, 14 Feb 2020 20:47:34 +0000 Subject: [PATCH 0267/2231] xhci: Add missing annotation for xhci_enter_test_mode Sparse reports a warning at xhci_enter_test_mode() warning: context imbalance in xhci_enter_test_mode - unexpected unlock The root cause is the missing annotation at xhci_enter_test_mode() Add the missing __must_hold(&xhci->lock) annotattion Signed-off-by: Jules Irenge Link: https://lore.kernel.org/r/20200214204741.94112-24-jbi.octave@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 57e3f2c5475a..38839308557e 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -609,6 +609,7 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci, static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, u16 wIndex, unsigned long *flags) + __must_hold(&xhci->lock) { int i, retval; From a30f8a91f3c25ad7df897bf7877bad410ad19f79 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Mon, 17 Feb 2020 17:42:55 +0800 Subject: [PATCH 0268/2231] cpufreq: imx-cpufreq-dt: Add "cpu-supply" property check The cpufreq-dt driver allows cpufreq driver enabled without valid regulator assigned, however, all i.MX platforms using cpufreq-dt driver now require valid regulator, add "cpu-supply" property check to avoid i.MX platforms' cpufreq enabled without valid regulator and lead to system unstable. Signed-off-by: Anson Huang Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx-cpufreq-dt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/imx-cpufreq-dt.c b/drivers/cpufreq/imx-cpufreq-dt.c index 6cb8193421ea..0e29d88618c9 100644 --- a/drivers/cpufreq/imx-cpufreq-dt.c +++ b/drivers/cpufreq/imx-cpufreq-dt.c @@ -31,6 +31,9 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) int speed_grade, mkt_segment; int ret; + if (!of_find_property(cpu_dev->of_node, "cpu-supply", NULL)) + return -ENODEV; + ret = nvmem_cell_read_u32(cpu_dev, "speed_grade", &cell_value); if (ret) return ret; From bea1bfd5b7226ac7c3f93f76be89221f33222a34 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Wed, 12 Feb 2020 13:03:40 +0100 Subject: [PATCH 0269/2231] EDAC/mc: Change mci device removal to use put_device() There are dimm and csrow devices linked to the mci device esp. to show up in sysfs. It must be granted that children devices are removed before its mci parent. Thus, the release functions must be called in the correct order and may not miss any child before releasing its parent. In the current implementation this is only granted by the correct order of release functions. A much better approach is to use put_device() that releases the device only after all users are gone. It is the recommended way to release a device and free its memory. The function uses the device's refcount and only frees it if there are no users of it anymore such as children. So implement a mci_release() function to remove mci devices, use put_device() to free them and early initialize the mci device right after its struct has been allocated. Change the release function so that it can be universally used no matter if the device is registered or not. Since subsequent dimm and csrow sysfs links are implemented as children devices, their refcounts will keep the parent mci device from being removed as long as sysfs entries exist and until all users have been unregistered in edac_remove_sysfs_mci_device(). Remove edac_unregister_sysfs() and merge mci sysfs removal into edac_remove_sysfs_mci_device(). There is only a single instance now that removes the sysfs entries. The function can now be used in the error paths for cleanup. Also, create device release functions for all involved devices (dev->release), remove device_type release functions (dev_type-> release) and also use dev->init_name instead of dev_set_name(). [ bp: Massage commit message and comments. ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200212120340.4764-5-rrichter@marvell.com --- drivers/edac/edac_mc.c | 12 +++-- drivers/edac/edac_mc_sysfs.c | 90 +++++++++++++++--------------------- drivers/edac/edac_module.h | 1 - 3 files changed, 47 insertions(+), 56 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 69e0d90460e6..64785e644482 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -278,6 +278,12 @@ void *edac_align_ptr(void **p, unsigned int size, int n_elems) static void _edac_mc_free(struct mem_ctl_info *mci) { + put_device(&mci->dev); +} + +static void mci_release(struct device *dev) +{ + struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); struct csrow_info *csr; int i, chn, row; @@ -371,6 +377,9 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, if (mci == NULL) return NULL; + mci->dev.release = mci_release; + device_initialize(&mci->dev); + /* Adjust pointers so they point within the memory we just allocated * rather than an imaginary chunk of memory located at address 0. */ @@ -505,9 +514,6 @@ void edac_mc_free(struct mem_ctl_info *mci) { edac_dbg(1, "\n"); - if (device_is_registered(&mci->dev)) - edac_unregister_sysfs(mci); - _edac_mc_free(mci); } EXPORT_SYMBOL_GPL(edac_mc_free); diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index c70ec0a306d8..11e1b436f116 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -274,14 +274,8 @@ static const struct attribute_group *csrow_attr_groups[] = { NULL }; -static void csrow_attr_release(struct device *dev) -{ - /* release device with _edac_mc_free() */ -} - static const struct device_type csrow_attr_type = { .groups = csrow_attr_groups, - .release = csrow_attr_release, }; /* @@ -387,6 +381,14 @@ static const struct attribute_group *csrow_dev_groups[] = { NULL }; +static void csrow_release(struct device *dev) +{ + /* + * Nothing to do, just unregister sysfs here. The mci + * device owns the data and will also release it. + */ +} + static inline int nr_pages_per_csrow(struct csrow_info *csrow) { int chan, nr_pages = 0; @@ -405,6 +407,7 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci, csrow->dev.type = &csrow_attr_type; csrow->dev.groups = csrow_dev_groups; + csrow->dev.release = csrow_release; device_initialize(&csrow->dev); csrow->dev.parent = &mci->dev; csrow->mci = mci; @@ -441,10 +444,8 @@ static int edac_create_csrow_objects(struct mem_ctl_info *mci) error: for (--i; i >= 0; i--) { - csrow = mci->csrows[i]; - if (!nr_pages_per_csrow(csrow)) - continue; - device_unregister(&mci->csrows[i]->dev); + if (device_is_registered(&mci->csrows[i]->dev)) + device_unregister(&mci->csrows[i]->dev); } return err; @@ -453,15 +454,13 @@ error: static void edac_delete_csrow_objects(struct mem_ctl_info *mci) { int i; - struct csrow_info *csrow; - for (i = mci->nr_csrows - 1; i >= 0; i--) { - csrow = mci->csrows[i]; - if (!nr_pages_per_csrow(csrow)) - continue; - device_unregister(&mci->csrows[i]->dev); + for (i = 0; i < mci->nr_csrows; i++) { + if (device_is_registered(&mci->csrows[i]->dev)) + device_unregister(&mci->csrows[i]->dev); } } + #endif /* @@ -602,16 +601,18 @@ static const struct attribute_group *dimm_attr_groups[] = { NULL }; -static void dimm_attr_release(struct device *dev) -{ - /* release device with _edac_mc_free() */ -} - static const struct device_type dimm_attr_type = { .groups = dimm_attr_groups, - .release = dimm_attr_release, }; +static void dimm_release(struct device *dev) +{ + /* + * Nothing to do, just unregister sysfs here. The mci + * device owns the data and will also release it. + */ +} + /* Create a DIMM object under specifed memory controller device */ static int edac_create_dimm_object(struct mem_ctl_info *mci, struct dimm_info *dimm) @@ -620,6 +621,7 @@ static int edac_create_dimm_object(struct mem_ctl_info *mci, dimm->mci = mci; dimm->dev.type = &dimm_attr_type; + dimm->dev.release = dimm_release; device_initialize(&dimm->dev); dimm->dev.parent = &mci->dev; @@ -884,14 +886,8 @@ static const struct attribute_group *mci_attr_groups[] = { NULL }; -static void mci_attr_release(struct device *dev) -{ - /* release device with _edac_mc_free() */ -} - static const struct device_type mci_attr_type = { .groups = mci_attr_groups, - .release = mci_attr_release, }; /* @@ -910,8 +906,6 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, /* get the /sys/devices/system/edac subsys reference */ mci->dev.type = &mci_attr_type; - device_initialize(&mci->dev); - mci->dev.parent = mci_pdev; mci->dev.groups = groups; dev_set_name(&mci->dev, "mc%d", mci->mc_idx); @@ -921,7 +915,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, err = device_add(&mci->dev); if (err < 0) { edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev)); - put_device(&mci->dev); + /* no put_device() here, free mci with _edac_mc_free() */ return err; } @@ -937,24 +931,20 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, err = edac_create_dimm_object(mci, dimm); if (err) - goto fail_unregister_dimm; + goto fail; } #ifdef CONFIG_EDAC_LEGACY_SYSFS err = edac_create_csrow_objects(mci); if (err < 0) - goto fail_unregister_dimm; + goto fail; #endif edac_create_debugfs_nodes(mci); return 0; -fail_unregister_dimm: - mci_for_each_dimm(mci, dimm) { - if (device_is_registered(&dimm->dev)) - device_unregister(&dimm->dev); - } - device_unregister(&mci->dev); +fail: + edac_remove_sysfs_mci_device(mci); return err; } @@ -966,6 +956,9 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) { struct dimm_info *dimm; + if (!device_is_registered(&mci->dev)) + return; + edac_dbg(0, "\n"); #ifdef CONFIG_EDAC_DEBUG @@ -976,17 +969,14 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) #endif mci_for_each_dimm(mci, dimm) { - if (dimm->nr_pages == 0) + if (!device_is_registered(&dimm->dev)) continue; edac_dbg(1, "unregistering device %s\n", dev_name(&dimm->dev)); device_unregister(&dimm->dev); } -} -void edac_unregister_sysfs(struct mem_ctl_info *mci) -{ - edac_dbg(1, "unregistering device %s\n", dev_name(&mci->dev)); - device_unregister(&mci->dev); + /* only remove the device, but keep mci */ + device_del(&mci->dev); } static void mc_attr_release(struct device *dev) @@ -1000,9 +990,6 @@ static void mc_attr_release(struct device *dev) kfree(dev); } -static const struct device_type mc_attr_type = { - .release = mc_attr_release, -}; /* * Init/exit code for the module. Basically, creates/removes /sys/class/rc */ @@ -1015,11 +1002,10 @@ int __init edac_mc_sysfs_init(void) return -ENOMEM; mci_pdev->bus = edac_get_sysfs_subsys(); - mci_pdev->type = &mc_attr_type; - device_initialize(mci_pdev); - dev_set_name(mci_pdev, "mc"); + mci_pdev->release = mc_attr_release; + mci_pdev->init_name = "mc"; - err = device_add(mci_pdev); + err = device_register(mci_pdev); if (err < 0) { edac_dbg(1, "failure: create device %s\n", dev_name(mci_pdev)); put_device(mci_pdev); diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index 388427d378b1..aa1f91688eb8 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h @@ -28,7 +28,6 @@ void edac_mc_sysfs_exit(void); extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, const struct attribute_group **groups); extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); -void edac_unregister_sysfs(struct mem_ctl_info *mci); extern int edac_get_log_ue(void); extern int edac_get_log_ce(void); extern int edac_get_panic_on_ue(void); From aad28c6f6b092b7382ee8dfb455772f7018a4cd1 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:02:49 +0000 Subject: [PATCH 0270/2231] EDAC/mc: Split edac_mc_alloc() into smaller functions edac_mc_alloc() is huge. Factor out code by moving it to the two new functions edac_mc_alloc_csrows() and edac_mc_alloc_dimms(). Do not move code yet for better review. [ bp: sort local args in reversed fir tree order. ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-2-rrichter@marvell.com --- drivers/edac/edac_mc.c | 237 +++++++++++++++++++++++------------------ 1 file changed, 136 insertions(+), 101 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 64785e644482..71777b4798a4 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -311,6 +311,9 @@ static void mci_release(struct device *dev) kfree(mci); } +static int edac_mc_alloc_csrows(struct mem_ctl_info *mci); +static int edac_mc_alloc_dimms(struct mem_ctl_info *mci); + struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, unsigned int n_layers, struct edac_mc_layer *layers, @@ -318,15 +321,11 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, { struct mem_ctl_info *mci; struct edac_mc_layer *layer; - struct csrow_info *csr; - struct rank_info *chan; - struct dimm_info *dimm; u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; - unsigned int pos[EDAC_MAX_LAYERS]; unsigned int idx, size, tot_dimms = 1, count = 1; unsigned int tot_csrows = 1, tot_channels = 1, tot_errcount = 0; - void *pvt, *p, *ptr = NULL; - int i, j, row, chn, n, len; + void *pvt, *ptr = NULL; + int i; bool per_rank = false; if (WARN_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0)) @@ -401,103 +400,11 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, mci->num_cschannel = tot_channels; mci->csbased = per_rank; - /* - * Alocate and fill the csrow/channels structs - */ - mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL); - if (!mci->csrows) - goto error; - for (row = 0; row < tot_csrows; row++) { - csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL); - if (!csr) - goto error; - mci->csrows[row] = csr; - csr->csrow_idx = row; - csr->mci = mci; - csr->nr_channels = tot_channels; - csr->channels = kcalloc(tot_channels, sizeof(*csr->channels), - GFP_KERNEL); - if (!csr->channels) - goto error; - - for (chn = 0; chn < tot_channels; chn++) { - chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL); - if (!chan) - goto error; - csr->channels[chn] = chan; - chan->chan_idx = chn; - chan->csrow = csr; - } - } - - /* - * Allocate and fill the dimm structs - */ - mci->dimms = kcalloc(tot_dimms, sizeof(*mci->dimms), GFP_KERNEL); - if (!mci->dimms) + if (edac_mc_alloc_csrows(mci)) goto error; - memset(&pos, 0, sizeof(pos)); - row = 0; - chn = 0; - for (idx = 0; idx < tot_dimms; idx++) { - chan = mci->csrows[row]->channels[chn]; - - dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL); - if (!dimm) - goto error; - mci->dimms[idx] = dimm; - dimm->mci = mci; - dimm->idx = idx; - - /* - * Copy DIMM location and initialize it. - */ - len = sizeof(dimm->label); - p = dimm->label; - n = snprintf(p, len, "mc#%u", mc_num); - p += n; - len -= n; - for (j = 0; j < n_layers; j++) { - n = snprintf(p, len, "%s#%u", - edac_layer_name[layers[j].type], - pos[j]); - p += n; - len -= n; - dimm->location[j] = pos[j]; - - if (len <= 0) - break; - } - - /* Link it to the csrows old API data */ - chan->dimm = dimm; - dimm->csrow = row; - dimm->cschannel = chn; - - /* Increment csrow location */ - if (layers[0].is_virt_csrow) { - chn++; - if (chn == tot_channels) { - chn = 0; - row++; - } - } else { - row++; - if (row == tot_csrows) { - row = 0; - chn++; - } - } - - /* Increment dimm location */ - for (j = n_layers - 1; j >= 0; j--) { - pos[j]++; - if (pos[j] < layers[j].size) - break; - pos[j] = 0; - } - } + if (edac_mc_alloc_dimms(mci)) + goto error; mci->op_state = OP_ALLOC; @@ -510,6 +417,134 @@ error: } EXPORT_SYMBOL_GPL(edac_mc_alloc); +static int edac_mc_alloc_csrows(struct mem_ctl_info *mci) +{ + unsigned int tot_channels = mci->num_cschannel; + unsigned int tot_csrows = mci->nr_csrows; + unsigned int row, chn; + + /* + * Alocate and fill the csrow/channels structs + */ + mci->csrows = kcalloc(tot_csrows, sizeof(*mci->csrows), GFP_KERNEL); + if (!mci->csrows) + return -ENOMEM; + + for (row = 0; row < tot_csrows; row++) { + struct csrow_info *csr; + + csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL); + if (!csr) + return -ENOMEM; + + mci->csrows[row] = csr; + csr->csrow_idx = row; + csr->mci = mci; + csr->nr_channels = tot_channels; + csr->channels = kcalloc(tot_channels, sizeof(*csr->channels), + GFP_KERNEL); + if (!csr->channels) + return -ENOMEM; + + for (chn = 0; chn < tot_channels; chn++) { + struct rank_info *chan; + + chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + csr->channels[chn] = chan; + chan->chan_idx = chn; + chan->csrow = csr; + } + } + + return 0; +} + +static int edac_mc_alloc_dimms(struct mem_ctl_info *mci) +{ + unsigned int pos[EDAC_MAX_LAYERS]; + unsigned int row, chn, idx; + int layer; + void *p; + + /* + * Allocate and fill the dimm structs + */ + mci->dimms = kcalloc(mci->tot_dimms, sizeof(*mci->dimms), GFP_KERNEL); + if (!mci->dimms) + return -ENOMEM; + + memset(&pos, 0, sizeof(pos)); + row = 0; + chn = 0; + for (idx = 0; idx < mci->tot_dimms; idx++) { + struct dimm_info *dimm; + struct rank_info *chan; + int n, len; + + chan = mci->csrows[row]->channels[chn]; + + dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL); + if (!dimm) + return -ENOMEM; + mci->dimms[idx] = dimm; + dimm->mci = mci; + dimm->idx = idx; + + /* + * Copy DIMM location and initialize it. + */ + len = sizeof(dimm->label); + p = dimm->label; + n = snprintf(p, len, "mc#%u", mci->mc_idx); + p += n; + len -= n; + for (layer = 0; layer < mci->n_layers; layer++) { + n = snprintf(p, len, "%s#%u", + edac_layer_name[mci->layers[layer].type], + pos[layer]); + p += n; + len -= n; + dimm->location[layer] = pos[layer]; + + if (len <= 0) + break; + } + + /* Link it to the csrows old API data */ + chan->dimm = dimm; + dimm->csrow = row; + dimm->cschannel = chn; + + /* Increment csrow location */ + if (mci->layers[0].is_virt_csrow) { + chn++; + if (chn == mci->num_cschannel) { + chn = 0; + row++; + } + } else { + row++; + if (row == mci->nr_csrows) { + row = 0; + chn++; + } + } + + /* Increment dimm location */ + for (layer = mci->n_layers - 1; layer >= 0; layer--) { + pos[layer]++; + if (pos[layer] < mci->layers[layer].size) + break; + pos[layer] = 0; + } + } + + return 0; +} + void edac_mc_free(struct mem_ctl_info *mci) { edac_dbg(1, "\n"); From 1f27c79062385af38c7c075f0fc174f98164ab2e Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:02:52 +0000 Subject: [PATCH 0271/2231] EDAC/mc: Reorder functions edac_mc_alloc*() Reorder the new created functions edac_mc_alloc_csrows() and edac_mc_alloc_dimms() and move them before edac_mc_alloc(). No further code changes. Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-3-rrichter@marvell.com --- drivers/edac/edac_mc.c | 209 ++++++++++++++++++++--------------------- 1 file changed, 103 insertions(+), 106 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 71777b4798a4..25130789e996 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -311,112 +311,6 @@ static void mci_release(struct device *dev) kfree(mci); } -static int edac_mc_alloc_csrows(struct mem_ctl_info *mci); -static int edac_mc_alloc_dimms(struct mem_ctl_info *mci); - -struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, - unsigned int n_layers, - struct edac_mc_layer *layers, - unsigned int sz_pvt) -{ - struct mem_ctl_info *mci; - struct edac_mc_layer *layer; - u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; - unsigned int idx, size, tot_dimms = 1, count = 1; - unsigned int tot_csrows = 1, tot_channels = 1, tot_errcount = 0; - void *pvt, *ptr = NULL; - int i; - bool per_rank = false; - - if (WARN_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0)) - return NULL; - - /* - * Calculate the total amount of dimms and csrows/cschannels while - * in the old API emulation mode - */ - for (idx = 0; idx < n_layers; idx++) { - tot_dimms *= layers[idx].size; - - if (layers[idx].is_virt_csrow) - tot_csrows *= layers[idx].size; - else - tot_channels *= layers[idx].size; - - if (layers[idx].type == EDAC_MC_LAYER_CHIP_SELECT) - per_rank = true; - } - - /* Figure out the offsets of the various items from the start of an mc - * structure. We want the alignment of each item to be at least as - * stringent as what the compiler would provide if we could simply - * hardcode everything into a single struct. - */ - mci = edac_align_ptr(&ptr, sizeof(*mci), 1); - layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); - for (i = 0; i < n_layers; i++) { - count *= layers[i].size; - edac_dbg(4, "errcount layer %d size %d\n", i, count); - ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); - ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); - tot_errcount += 2 * count; - } - - edac_dbg(4, "allocating %d error counters\n", tot_errcount); - pvt = edac_align_ptr(&ptr, sz_pvt, 1); - size = ((unsigned long)pvt) + sz_pvt; - - edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n", - size, - tot_dimms, - per_rank ? "ranks" : "dimms", - tot_csrows * tot_channels); - - mci = kzalloc(size, GFP_KERNEL); - if (mci == NULL) - return NULL; - - mci->dev.release = mci_release; - device_initialize(&mci->dev); - - /* Adjust pointers so they point within the memory we just allocated - * rather than an imaginary chunk of memory located at address 0. - */ - layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer)); - for (i = 0; i < n_layers; i++) { - mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i])); - mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i])); - } - pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL; - - /* setup index and various internal pointers */ - mci->mc_idx = mc_num; - mci->tot_dimms = tot_dimms; - mci->pvt_info = pvt; - mci->n_layers = n_layers; - mci->layers = layer; - memcpy(mci->layers, layers, sizeof(*layer) * n_layers); - mci->nr_csrows = tot_csrows; - mci->num_cschannel = tot_channels; - mci->csbased = per_rank; - - if (edac_mc_alloc_csrows(mci)) - goto error; - - if (edac_mc_alloc_dimms(mci)) - goto error; - - mci->op_state = OP_ALLOC; - - return mci; - -error: - _edac_mc_free(mci); - - return NULL; -} -EXPORT_SYMBOL_GPL(edac_mc_alloc); - static int edac_mc_alloc_csrows(struct mem_ctl_info *mci) { unsigned int tot_channels = mci->num_cschannel; @@ -545,6 +439,109 @@ static int edac_mc_alloc_dimms(struct mem_ctl_info *mci) return 0; } +struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, + unsigned int n_layers, + struct edac_mc_layer *layers, + unsigned int sz_pvt) +{ + struct mem_ctl_info *mci; + struct edac_mc_layer *layer; + u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; + unsigned int idx, size, tot_dimms = 1, count = 1; + unsigned int tot_csrows = 1, tot_channels = 1, tot_errcount = 0; + void *pvt, *ptr = NULL; + int i; + bool per_rank = false; + + if (WARN_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0)) + return NULL; + + /* + * Calculate the total amount of dimms and csrows/cschannels while + * in the old API emulation mode + */ + for (idx = 0; idx < n_layers; idx++) { + tot_dimms *= layers[idx].size; + + if (layers[idx].is_virt_csrow) + tot_csrows *= layers[idx].size; + else + tot_channels *= layers[idx].size; + + if (layers[idx].type == EDAC_MC_LAYER_CHIP_SELECT) + per_rank = true; + } + + /* Figure out the offsets of the various items from the start of an mc + * structure. We want the alignment of each item to be at least as + * stringent as what the compiler would provide if we could simply + * hardcode everything into a single struct. + */ + mci = edac_align_ptr(&ptr, sizeof(*mci), 1); + layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); + for (i = 0; i < n_layers; i++) { + count *= layers[i].size; + edac_dbg(4, "errcount layer %d size %d\n", i, count); + ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); + ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); + tot_errcount += 2 * count; + } + + edac_dbg(4, "allocating %d error counters\n", tot_errcount); + pvt = edac_align_ptr(&ptr, sz_pvt, 1); + size = ((unsigned long)pvt) + sz_pvt; + + edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n", + size, + tot_dimms, + per_rank ? "ranks" : "dimms", + tot_csrows * tot_channels); + + mci = kzalloc(size, GFP_KERNEL); + if (mci == NULL) + return NULL; + + mci->dev.release = mci_release; + device_initialize(&mci->dev); + + /* Adjust pointers so they point within the memory we just allocated + * rather than an imaginary chunk of memory located at address 0. + */ + layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer)); + for (i = 0; i < n_layers; i++) { + mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i])); + mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i])); + } + pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL; + + /* setup index and various internal pointers */ + mci->mc_idx = mc_num; + mci->tot_dimms = tot_dimms; + mci->pvt_info = pvt; + mci->n_layers = n_layers; + mci->layers = layer; + memcpy(mci->layers, layers, sizeof(*layer) * n_layers); + mci->nr_csrows = tot_csrows; + mci->num_cschannel = tot_channels; + mci->csbased = per_rank; + + if (edac_mc_alloc_csrows(mci)) + goto error; + + if (edac_mc_alloc_dimms(mci)) + goto error; + + mci->op_state = OP_ALLOC; + + return mci; + +error: + _edac_mc_free(mci); + + return NULL; +} +EXPORT_SYMBOL_GPL(edac_mc_alloc); + void edac_mc_free(struct mem_ctl_info *mci) { edac_dbg(1, "\n"); From 672ef0e5684048a00aeb923b10131275ea688543 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:02:54 +0000 Subject: [PATCH 0272/2231] EDAC: Store error type in struct edac_raw_error_desc Store the error type in struct edac_raw_error_desc. This makes the type parameter of edac_raw_mc_handle_error() obsolete. [ kernel-doc typo ] Reported-by: kbuild test robot Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-4-rrichter@marvell.com --- drivers/edac/edac_mc.c | 10 +++++----- drivers/edac/edac_mc.h | 4 +--- drivers/edac/ghes_edac.c | 11 +++++------ include/linux/edac.h | 2 ++ 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 25130789e996..8d8c0997d41a 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -1084,8 +1084,7 @@ static void edac_ue_error(struct mem_ctl_info *mci, edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count); } -void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, - struct mem_ctl_info *mci, +void edac_raw_mc_handle_error(struct mem_ctl_info *mci, struct edac_raw_error_desc *e) { char detail[80]; @@ -1100,14 +1099,14 @@ void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, /* Report the error via the trace interface */ if (IS_ENABLED(CONFIG_RAS)) - trace_mc_event(type, e->msg, e->label, e->error_count, + trace_mc_event(e->type, e->msg, e->label, e->error_count, mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer, (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page, grain_bits, e->syndrome, e->other_detail); /* Memory type dependent details about the error */ - if (type == HW_EVENT_ERR_CORRECTED) { + if (e->type == HW_EVENT_ERR_CORRECTED) { snprintf(detail, sizeof(detail), "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", e->page_frame_number, e->offset_in_page, @@ -1152,6 +1151,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, /* Fills the error report buffer */ memset(e, 0, sizeof (*e)); e->error_count = error_count; + e->type = type; e->top_layer = top_layer; e->mid_layer = mid_layer; e->low_layer = low_layer; @@ -1282,6 +1282,6 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, if (p > e->location) *(p - 1) = '\0'; - edac_raw_mc_handle_error(type, mci, e); + edac_raw_mc_handle_error(mci, e); } EXPORT_SYMBOL_GPL(edac_mc_handle_error); diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h index 02aac5c61d00..5d78be774f9e 100644 --- a/drivers/edac/edac_mc.h +++ b/drivers/edac/edac_mc.h @@ -212,7 +212,6 @@ extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, * edac_raw_mc_handle_error() - Reports a memory event to userspace without * doing anything to discover the error location. * - * @type: severity of the error (CE/UE/Fatal) * @mci: a struct mem_ctl_info pointer * @e: error description * @@ -220,8 +219,7 @@ extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, * only be called directly when the hardware error come directly from BIOS, * like in the case of APEI GHES driver. */ -void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, - struct mem_ctl_info *mci, +void edac_raw_mc_handle_error(struct mem_ctl_info *mci, struct edac_raw_error_desc *e); /** diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index b99080d8a10c..7c3e5264a41e 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -201,7 +201,6 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg) void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) { - enum hw_event_mc_err_type type; struct edac_raw_error_desc *e; struct mem_ctl_info *mci; struct ghes_edac_pvt *pvt; @@ -240,17 +239,17 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) switch (sev) { case GHES_SEV_CORRECTED: - type = HW_EVENT_ERR_CORRECTED; + e->type = HW_EVENT_ERR_CORRECTED; break; case GHES_SEV_RECOVERABLE: - type = HW_EVENT_ERR_UNCORRECTED; + e->type = HW_EVENT_ERR_UNCORRECTED; break; case GHES_SEV_PANIC: - type = HW_EVENT_ERR_FATAL; + e->type = HW_EVENT_ERR_FATAL; break; default: case GHES_SEV_NO: - type = HW_EVENT_ERR_INFO; + e->type = HW_EVENT_ERR_INFO; } edac_dbg(1, "error validation_bits: 0x%08llx\n", @@ -442,7 +441,7 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) if (p > pvt->other_detail) *(p - 1) = '\0'; - edac_raw_mc_handle_error(type, mci, e); + edac_raw_mc_handle_error(mci, e); unlock: spin_unlock_irqrestore(&ghes_lock, flags); diff --git a/include/linux/edac.h b/include/linux/edac.h index cc31b9742684..6703eb492cd2 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -442,6 +442,7 @@ struct errcount_attribute_data { * struct edac_raw_error_desc - Raw error report structure * @grain: minimum granularity for an error report, in bytes * @error_count: number of errors of the same type + * @type: severity of the error (CE/UE/Fatal) * @top_layer: top layer of the error (layer[0]) * @mid_layer: middle layer of the error (layer[1]) * @low_layer: low layer of the error (layer[2]) @@ -462,6 +463,7 @@ struct edac_raw_error_desc { long grain; u16 error_count; + enum hw_event_mc_err_type type; int top_layer; int mid_layer; int low_layer; From 91b327f6728b0c4dfa089f11a474789854baa0b1 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:02:56 +0000 Subject: [PATCH 0273/2231] EDAC/mc: Determine mci pointer from the error descriptor Each struct mci has its own error descriptor. Create a function error_desc_to_mci() to determine the corresponding mci from an error descriptor. This removes @mci from the parameter list of edac_raw_mc_handle_error() as the mci pointer does not need to be passed any longer. [ bp: Massage commit message. ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-5-rrichter@marvell.com --- drivers/edac/edac_mc.c | 11 ++++++++--- drivers/edac/edac_mc.h | 4 +--- drivers/edac/ghes_edac.c | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 8d8c0997d41a..bc8d1d7b21a0 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -55,6 +55,11 @@ static LIST_HEAD(mc_devices); */ static const char *edac_mc_owner; +static struct mem_ctl_info *error_desc_to_mci(struct edac_raw_error_desc *e) +{ + return container_of(e, struct mem_ctl_info, error_desc); +} + int edac_get_report_status(void) { return edac_report; @@ -1084,9 +1089,9 @@ static void edac_ue_error(struct mem_ctl_info *mci, edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count); } -void edac_raw_mc_handle_error(struct mem_ctl_info *mci, - struct edac_raw_error_desc *e) +void edac_raw_mc_handle_error(struct edac_raw_error_desc *e) { + struct mem_ctl_info *mci = error_desc_to_mci(e); char detail[80]; int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; u8 grain_bits; @@ -1282,6 +1287,6 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, if (p > e->location) *(p - 1) = '\0'; - edac_raw_mc_handle_error(mci, e); + edac_raw_mc_handle_error(e); } EXPORT_SYMBOL_GPL(edac_mc_handle_error); diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h index 5d78be774f9e..881b00eadf7a 100644 --- a/drivers/edac/edac_mc.h +++ b/drivers/edac/edac_mc.h @@ -212,15 +212,13 @@ extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, * edac_raw_mc_handle_error() - Reports a memory event to userspace without * doing anything to discover the error location. * - * @mci: a struct mem_ctl_info pointer * @e: error description * * This raw function is used internally by edac_mc_handle_error(). It should * only be called directly when the hardware error come directly from BIOS, * like in the case of APEI GHES driver. */ -void edac_raw_mc_handle_error(struct mem_ctl_info *mci, - struct edac_raw_error_desc *e); +void edac_raw_mc_handle_error(struct edac_raw_error_desc *e); /** * edac_mc_handle_error() - Reports a memory event to userspace. diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index 7c3e5264a41e..bef8a428c429 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -441,7 +441,7 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) if (p > pvt->other_detail) *(p - 1) = '\0'; - edac_raw_mc_handle_error(mci, e); + edac_raw_mc_handle_error(e); unlock: spin_unlock_irqrestore(&ghes_lock, flags); From 6334dc4e3ff53031a2d522b826c4fab92cfdea93 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 14 Feb 2020 15:17:56 +0100 Subject: [PATCH 0274/2231] EDAC/mc: Carve out error increment into a separate function Carve out the error_count increment into a separate function edac_inc_csrow(). This better separates code and reduces the indentation level. Implementation note: The function edac_inc_csrow() counts the same as before, ->ce_count is only incremented if row >= 0. This is esp. true for the case of (!e->enable_per_layer_report). Here, a DIMM was not found, variable row still has a value of -1 and ->ce_count is not incremented. [ bp: Massage commit message. ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200214141757.8976-1-rrichter@marvell.com --- drivers/edac/edac_mc.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index bc8d1d7b21a0..35d282492505 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -1089,6 +1089,26 @@ static void edac_ue_error(struct mem_ctl_info *mci, edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count); } +static void edac_inc_csrow(struct edac_raw_error_desc *e, int row, int chan) +{ + struct mem_ctl_info *mci = error_desc_to_mci(e); + enum hw_event_mc_err_type type = e->type; + u16 count = e->error_count; + + if (row < 0) + return; + + edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan); + + if (type == HW_EVENT_ERR_CORRECTED) { + mci->csrows[row]->ce_count += count; + if (chan >= 0) + mci->csrows[row]->channels[chan]->ce_count += count; + } else { + mci->csrows[row]->ue_count += count; + } +} + void edac_raw_mc_handle_error(struct edac_raw_error_desc *e) { struct mem_ctl_info *mci = error_desc_to_mci(e); @@ -1256,22 +1276,12 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, chan = -2; } - if (!e->enable_per_layer_report) { + if (!e->enable_per_layer_report) strcpy(e->label, "any memory"); - } else { - edac_dbg(4, "csrow/channel to increment: (%d,%d)\n", row, chan); - if (p == e->label) - strcpy(e->label, "unknown memory"); - if (type == HW_EVENT_ERR_CORRECTED) { - if (row >= 0) { - mci->csrows[row]->ce_count += error_count; - if (chan >= 0) - mci->csrows[row]->channels[chan]->ce_count += error_count; - } - } else - if (row >= 0) - mci->csrows[row]->ue_count += error_count; - } + else if (!*e->label) + strcpy(e->label, "unknown memory"); + + edac_inc_csrow(e, row, chan); /* Fill the RAM location data */ p = e->location; From 65bb4d1af92cf007adc0a0c59dadcc393c5cada6 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:03:00 +0000 Subject: [PATCH 0275/2231] EDAC/mc: Report "unknown memory" on too many DIMM labels found There is a limitation to report only EDAC_MAX_LABELS in e->label of the error descriptor. This is to prevent a potential string overflow. The current implementation falls back to "any memory" in this case and also stops all further processing to find a unique row and channel of the possible error location. Reporting "any memory" is wrong as the memory controller reported an error location for one of the layers. Instead, report "unknown memory" and also do not break early in the loop to further check row and channel for uniqueness. [ bp: Massage commit message. ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-7-rrichter@marvell.com --- drivers/edac/edac_mc.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 35d282492505..7a1445feb59d 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -1243,20 +1243,21 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, * channel/memory controller/... may be affected. * Also, don't show errors for empty DIMM slots. */ - if (!e->enable_per_layer_report || !dimm->nr_pages) + if (!dimm->nr_pages) continue; - if (n_labels >= EDAC_MAX_LABELS) { - e->enable_per_layer_report = false; - break; - } n_labels++; - if (p != e->label) { - strcpy(p, OTHER_LABEL); - p += strlen(OTHER_LABEL); + if (n_labels > EDAC_MAX_LABELS) { + p = e->label; + *p = '\0'; + } else { + if (p != e->label) { + strcpy(p, OTHER_LABEL); + p += strlen(OTHER_LABEL); + } + strcpy(p, dimm->label); + p += strlen(p); } - strcpy(p, dimm->label); - p += strlen(p); /* * get csrow/channel of the DIMM, in order to allow From 67792cf9583c7816667c6b90007b5840f1b471f4 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:03:02 +0000 Subject: [PATCH 0276/2231] EDAC/mc: Remove enable_per_layer_report function argument Many functions carry the enable_per_layer_report argument. This is a bool value indicating the error information contains some location data where the error occurred. This can easily being determined by checking the pos[] array for values. Negative values indicate there is no location available. So if the top layer is negative, the error location is unknown. Just check if the top layer is negative and remove enable_per_layer_report as function argument and also from struct edac_raw_error_desc. [ bp: Reflow comments to 80 columns, while at it. ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-8-rrichter@marvell.com --- drivers/edac/edac_mc.c | 40 ++++++++++++++++++---------------------- drivers/edac/ghes_edac.c | 5 +---- include/linux/edac.h | 3 --- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 7a1445feb59d..1867f11a3438 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -946,7 +946,6 @@ const char *edac_layer_name[] = { EXPORT_SYMBOL_GPL(edac_layer_name); static void edac_inc_ce_error(struct mem_ctl_info *mci, - bool enable_per_layer_report, const int pos[EDAC_MAX_LAYERS], const u16 count) { @@ -954,7 +953,7 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci, mci->ce_mc += count; - if (!enable_per_layer_report) { + if (pos[0] < 0) { mci->ce_noinfo_count += count; return; } @@ -971,7 +970,6 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci, } static void edac_inc_ue_error(struct mem_ctl_info *mci, - bool enable_per_layer_report, const int pos[EDAC_MAX_LAYERS], const u16 count) { @@ -979,7 +977,7 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci, mci->ue_mc += count; - if (!enable_per_layer_report) { + if (pos[0] < 0) { mci->ue_noinfo_count += count; return; } @@ -1003,7 +1001,6 @@ static void edac_ce_error(struct mem_ctl_info *mci, const char *label, const char *detail, const char *other_detail, - const bool enable_per_layer_report, const unsigned long page_frame_number, const unsigned long offset_in_page, long grain) @@ -1026,7 +1023,7 @@ static void edac_ce_error(struct mem_ctl_info *mci, error_count, msg, msg_aux, label, location, detail); } - edac_inc_ce_error(mci, enable_per_layer_report, pos, error_count); + edac_inc_ce_error(mci, pos, error_count); if (mci->scrub_mode == SCRUB_SW_SRC) { /* @@ -1056,8 +1053,7 @@ static void edac_ue_error(struct mem_ctl_info *mci, const char *location, const char *label, const char *detail, - const char *other_detail, - const bool enable_per_layer_report) + const char *other_detail) { char *msg_aux = ""; @@ -1086,7 +1082,7 @@ static void edac_ue_error(struct mem_ctl_info *mci, msg, msg_aux, label, location, detail); } - edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count); + edac_inc_ue_error(mci, pos, error_count); } static void edac_inc_csrow(struct edac_raw_error_desc *e, int row, int chan) @@ -1136,16 +1132,16 @@ void edac_raw_mc_handle_error(struct edac_raw_error_desc *e) "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", e->page_frame_number, e->offset_in_page, e->grain, e->syndrome); - edac_ce_error(mci, e->error_count, pos, e->msg, e->location, e->label, - detail, e->other_detail, e->enable_per_layer_report, + edac_ce_error(mci, e->error_count, pos, e->msg, e->location, + e->label, detail, e->other_detail, e->page_frame_number, e->offset_in_page, e->grain); } else { snprintf(detail, sizeof(detail), "page:0x%lx offset:0x%lx grain:%ld", e->page_frame_number, e->offset_in_page, e->grain); - edac_ue_error(mci, e->error_count, pos, e->msg, e->location, e->label, - detail, e->other_detail, e->enable_per_layer_report); + edac_ue_error(mci, e->error_count, pos, e->msg, e->location, + e->label, detail, e->other_detail); } @@ -1170,6 +1166,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer }; int i, n_labels = 0; struct edac_raw_error_desc *e = &mci->error_desc; + bool any_memory = true; edac_dbg(3, "MC%d\n", mci->mc_idx); @@ -1187,10 +1184,9 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, e->other_detail = other_detail; /* - * Check if the event report is consistent and if the memory - * location is known. If it is known, enable_per_layer_report will be - * true, the DIMM(s) label info will be filled and the per-layer - * error counters will be incremented. + * Check if the event report is consistent and if the memory location is + * known. If it is, the DIMM(s) label info will be filled and the + * per-layer error counters will be incremented. */ for (i = 0; i < mci->n_layers; i++) { if (pos[i] >= (int)mci->layers[i].size) { @@ -1208,7 +1204,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, pos[i] = -1; } if (pos[i] >= 0) - e->enable_per_layer_report = true; + any_memory = false; } /* @@ -1239,9 +1235,9 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, /* * If the error is memory-controller wide, there's no need to - * seek for the affected DIMMs because the whole - * channel/memory controller/... may be affected. - * Also, don't show errors for empty DIMM slots. + * seek for the affected DIMMs because the whole channel/memory + * controller/... may be affected. Also, don't show errors for + * empty DIMM slots. */ if (!dimm->nr_pages) continue; @@ -1277,7 +1273,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, chan = -2; } - if (!e->enable_per_layer_report) + if (any_memory) strcpy(e->label, "any memory"); else if (!*e->label) strcpy(e->label, "unknown memory"); diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index bef8a428c429..cb3dab56a875 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -355,11 +355,8 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) mem_err->mem_dev_handle); index = get_dimm_smbios_index(mci, mem_err->mem_dev_handle); - if (index >= 0) { + if (index >= 0) e->top_layer = index; - e->enable_per_layer_report = true; - } - } if (p > e->location) *(p - 1) = '\0'; diff --git a/include/linux/edac.h b/include/linux/edac.h index 6703eb492cd2..815f246e0abd 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -454,8 +454,6 @@ struct errcount_attribute_data { * @location: location of the error * @label: label of the affected DIMM(s) * @other_detail: other driver-specific detail about the error - * @enable_per_layer_report: if false, the error affects all layers - * (typically, a memory controller error) */ struct edac_raw_error_desc { char location[LOCATION_SIZE]; @@ -472,7 +470,6 @@ struct edac_raw_error_desc { unsigned long syndrome; const char *msg; const char *other_detail; - bool enable_per_layer_report; }; /* MEMORY controller information structure From 6ab76179adb82006aea05f438a0601561c162b8f Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:03:04 +0000 Subject: [PATCH 0277/2231] EDAC/mc: Pass the error descriptor to error reporting functions Most arguments of error reporting functions are already stored in the struct edac_raw_error_desc error descriptor. Pass the error descriptor to the functions and reduce the functions' argument list. [ bp: Sort function args in reverse fir tree order. ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-9-rrichter@marvell.com --- drivers/edac/edac_mc.c | 100 +++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 58 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 1867f11a3438..120c8c1a9d3a 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -945,16 +945,16 @@ const char *edac_layer_name[] = { }; EXPORT_SYMBOL_GPL(edac_layer_name); -static void edac_inc_ce_error(struct mem_ctl_info *mci, - const int pos[EDAC_MAX_LAYERS], - const u16 count) +static void edac_inc_ce_error(struct edac_raw_error_desc *e) { + int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; + struct mem_ctl_info *mci = error_desc_to_mci(e); int i, index = 0; - mci->ce_mc += count; + mci->ce_mc += e->error_count; if (pos[0] < 0) { - mci->ce_noinfo_count += count; + mci->ce_noinfo_count += e->error_count; return; } @@ -962,23 +962,23 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci, if (pos[i] < 0) break; index += pos[i]; - mci->ce_per_layer[i][index] += count; + mci->ce_per_layer[i][index] += e->error_count; if (i < mci->n_layers - 1) index *= mci->layers[i + 1].size; } } -static void edac_inc_ue_error(struct mem_ctl_info *mci, - const int pos[EDAC_MAX_LAYERS], - const u16 count) +static void edac_inc_ue_error(struct edac_raw_error_desc *e) { + int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; + struct mem_ctl_info *mci = error_desc_to_mci(e); int i, index = 0; - mci->ue_mc += count; + mci->ue_mc += e->error_count; if (pos[0] < 0) { - mci->ue_noinfo_count += count; + mci->ue_noinfo_count += e->error_count; return; } @@ -986,44 +986,37 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci, if (pos[i] < 0) break; index += pos[i]; - mci->ue_per_layer[i][index] += count; + mci->ue_per_layer[i][index] += e->error_count; if (i < mci->n_layers - 1) index *= mci->layers[i + 1].size; } } -static void edac_ce_error(struct mem_ctl_info *mci, - const u16 error_count, - const int pos[EDAC_MAX_LAYERS], - const char *msg, - const char *location, - const char *label, - const char *detail, - const char *other_detail, - const unsigned long page_frame_number, - const unsigned long offset_in_page, - long grain) +static void edac_ce_error(struct edac_raw_error_desc *e, + const char *detail) { + struct mem_ctl_info *mci = error_desc_to_mci(e); unsigned long remapped_page; char *msg_aux = ""; - if (*msg) + if (*e->msg) msg_aux = " "; if (edac_mc_get_log_ce()) { - if (other_detail && *other_detail) + if (e->other_detail && *e->other_detail) edac_mc_printk(mci, KERN_WARNING, "%d CE %s%son %s (%s %s - %s)\n", - error_count, msg, msg_aux, label, - location, detail, other_detail); + e->error_count, e->msg, msg_aux, e->label, + e->location, detail, e->other_detail); else edac_mc_printk(mci, KERN_WARNING, "%d CE %s%son %s (%s %s)\n", - error_count, msg, msg_aux, label, - location, detail); + e->error_count, e->msg, msg_aux, e->label, + e->location, detail); } - edac_inc_ce_error(mci, pos, error_count); + + edac_inc_ce_error(e); if (mci->scrub_mode == SCRUB_SW_SRC) { /* @@ -1038,51 +1031,46 @@ static void edac_ce_error(struct mem_ctl_info *mci, * be scrubbed. */ remapped_page = mci->ctl_page_to_phys ? - mci->ctl_page_to_phys(mci, page_frame_number) : - page_frame_number; + mci->ctl_page_to_phys(mci, e->page_frame_number) : + e->page_frame_number; - edac_mc_scrub_block(remapped_page, - offset_in_page, grain); + edac_mc_scrub_block(remapped_page, e->offset_in_page, e->grain); } } -static void edac_ue_error(struct mem_ctl_info *mci, - const u16 error_count, - const int pos[EDAC_MAX_LAYERS], - const char *msg, - const char *location, - const char *label, - const char *detail, - const char *other_detail) +static void edac_ue_error(struct edac_raw_error_desc *e, + const char *detail) { + struct mem_ctl_info *mci = error_desc_to_mci(e); char *msg_aux = ""; - if (*msg) + if (*e->msg) msg_aux = " "; if (edac_mc_get_log_ue()) { - if (other_detail && *other_detail) + if (e->other_detail && *e->other_detail) edac_mc_printk(mci, KERN_WARNING, "%d UE %s%son %s (%s %s - %s)\n", - error_count, msg, msg_aux, label, - location, detail, other_detail); + e->error_count, e->msg, msg_aux, e->label, + e->location, detail, e->other_detail); else edac_mc_printk(mci, KERN_WARNING, "%d UE %s%son %s (%s %s)\n", - error_count, msg, msg_aux, label, - location, detail); + e->error_count, e->msg, msg_aux, e->label, + e->location, detail); } if (edac_mc_get_panic_on_ue()) { - if (other_detail && *other_detail) + if (e->other_detail && *e->other_detail) panic("UE %s%son %s (%s%s - %s)\n", - msg, msg_aux, label, location, detail, other_detail); + e->msg, msg_aux, e->label, e->location, detail, + e->other_detail); else panic("UE %s%son %s (%s%s)\n", - msg, msg_aux, label, location, detail); + e->msg, msg_aux, e->label, e->location, detail); } - edac_inc_ue_error(mci, pos, error_count); + edac_inc_ue_error(e); } static void edac_inc_csrow(struct edac_raw_error_desc *e, int row, int chan) @@ -1109,7 +1097,6 @@ void edac_raw_mc_handle_error(struct edac_raw_error_desc *e) { struct mem_ctl_info *mci = error_desc_to_mci(e); char detail[80]; - int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; u8 grain_bits; /* Sanity-check driver-supplied grain value. */ @@ -1132,16 +1119,13 @@ void edac_raw_mc_handle_error(struct edac_raw_error_desc *e) "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", e->page_frame_number, e->offset_in_page, e->grain, e->syndrome); - edac_ce_error(mci, e->error_count, pos, e->msg, e->location, - e->label, detail, e->other_detail, - e->page_frame_number, e->offset_in_page, e->grain); + edac_ce_error(e, detail); } else { snprintf(detail, sizeof(detail), "page:0x%lx offset:0x%lx grain:%ld", e->page_frame_number, e->offset_in_page, e->grain); - edac_ue_error(mci, e->error_count, pos, e->msg, e->location, - e->label, detail, e->other_detail); + edac_ue_error(e, detail); } From 1853ee72997645e1aea85e20b94f7dcc55636887 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:03:06 +0000 Subject: [PATCH 0278/2231] EDAC/mc: Remove detail[] string and cleanup error string generation The error descriptor is passed to the error reporting functions, so the error details can be directly generated there. Move string generation from edac_raw_mc_handle_error() to edac_ce_error() and edac_ue_error(). The intermediate detail[] string can be removed then. Also, cleanup the string generation by switching to a single variant only using the ternary operator. [ bp: put ternary operators on a separate line for better readability and use the short-form "inline if" in edac_mc_handle_error(). ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-10-rrichter@marvell.com --- drivers/edac/edac_mc.c | 90 +++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 58 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 120c8c1a9d3a..80629c702a4f 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -993,27 +993,20 @@ static void edac_inc_ue_error(struct edac_raw_error_desc *e) } } -static void edac_ce_error(struct edac_raw_error_desc *e, - const char *detail) +static void edac_ce_error(struct edac_raw_error_desc *e) { struct mem_ctl_info *mci = error_desc_to_mci(e); unsigned long remapped_page; - char *msg_aux = ""; - - if (*e->msg) - msg_aux = " "; if (edac_mc_get_log_ce()) { - if (e->other_detail && *e->other_detail) - edac_mc_printk(mci, KERN_WARNING, - "%d CE %s%son %s (%s %s - %s)\n", - e->error_count, e->msg, msg_aux, e->label, - e->location, detail, e->other_detail); - else - edac_mc_printk(mci, KERN_WARNING, - "%d CE %s%son %s (%s %s)\n", - e->error_count, e->msg, msg_aux, e->label, - e->location, detail); + edac_mc_printk(mci, KERN_WARNING, + "%d CE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx%s%s)\n", + e->error_count, e->msg, + *e->msg ? " " : "", + e->label, e->location, e->page_frame_number, e->offset_in_page, + e->grain, e->syndrome, + *e->other_detail ? " - " : "", + e->other_detail); } edac_inc_ce_error(e); @@ -1038,36 +1031,29 @@ static void edac_ce_error(struct edac_raw_error_desc *e, } } -static void edac_ue_error(struct edac_raw_error_desc *e, - const char *detail) +static void edac_ue_error(struct edac_raw_error_desc *e) { struct mem_ctl_info *mci = error_desc_to_mci(e); - char *msg_aux = ""; - - if (*e->msg) - msg_aux = " "; if (edac_mc_get_log_ue()) { - if (e->other_detail && *e->other_detail) - edac_mc_printk(mci, KERN_WARNING, - "%d UE %s%son %s (%s %s - %s)\n", - e->error_count, e->msg, msg_aux, e->label, - e->location, detail, e->other_detail); - else - edac_mc_printk(mci, KERN_WARNING, - "%d UE %s%son %s (%s %s)\n", - e->error_count, e->msg, msg_aux, e->label, - e->location, detail); + edac_mc_printk(mci, KERN_WARNING, + "%d UE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld%s%s)\n", + e->error_count, e->msg, + *e->msg ? " " : "", + e->label, e->location, e->page_frame_number, e->offset_in_page, + e->grain, + *e->other_detail ? " - " : "", + e->other_detail); } if (edac_mc_get_panic_on_ue()) { - if (e->other_detail && *e->other_detail) - panic("UE %s%son %s (%s%s - %s)\n", - e->msg, msg_aux, e->label, e->location, detail, - e->other_detail); - else - panic("UE %s%son %s (%s%s)\n", - e->msg, msg_aux, e->label, e->location, detail); + panic("UE %s%son %s (%s page:0x%lx offset:0x%lx grain:%ld%s%s)\n", + e->msg, + *e->msg ? " " : "", + e->label, e->location, e->page_frame_number, e->offset_in_page, + e->grain, + *e->other_detail ? " - " : "", + e->other_detail); } edac_inc_ue_error(e); @@ -1096,7 +1082,6 @@ static void edac_inc_csrow(struct edac_raw_error_desc *e, int row, int chan) void edac_raw_mc_handle_error(struct edac_raw_error_desc *e) { struct mem_ctl_info *mci = error_desc_to_mci(e); - char detail[80]; u8 grain_bits; /* Sanity-check driver-supplied grain value. */ @@ -1113,22 +1098,10 @@ void edac_raw_mc_handle_error(struct edac_raw_error_desc *e) (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page, grain_bits, e->syndrome, e->other_detail); - /* Memory type dependent details about the error */ - if (e->type == HW_EVENT_ERR_CORRECTED) { - snprintf(detail, sizeof(detail), - "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", - e->page_frame_number, e->offset_in_page, - e->grain, e->syndrome); - edac_ce_error(e, detail); - } else { - snprintf(detail, sizeof(detail), - "page:0x%lx offset:0x%lx grain:%ld", - e->page_frame_number, e->offset_in_page, e->grain); - - edac_ue_error(e, detail); - } - - + if (e->type == HW_EVENT_ERR_CORRECTED) + edac_ce_error(e); + else + edac_ue_error(e); } EXPORT_SYMBOL_GPL(edac_raw_mc_handle_error); @@ -1164,8 +1137,9 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, e->page_frame_number = page_frame_number; e->offset_in_page = offset_in_page; e->syndrome = syndrome; - e->msg = msg; - e->other_detail = other_detail; + /* need valid strings here for both: */ + e->msg = msg ?: ""; + e->other_detail = other_detail ?: ""; /* * Check if the event report is consistent and if the memory location is From 4aa92c86463273b673e4170c60cb78e2625781eb Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Mon, 17 Feb 2020 12:30:23 +0100 Subject: [PATCH 0279/2231] EDAC/mc: Remove per layer counters Looking at how mci->{ue,ce}_per_layer[EDAC_MAX_LAYERS] is used, it turns out that only the leaves in the memory hierarchy are consumed (in sysfs), but not the intermediate layers, e.g.: count = dimm->mci->ce_per_layer[dimm->mci->n_layers-1][dimm->idx]; These unused counters only add complexity, remove them. The error counter values are directly stored in struct dimm_info now. Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-11-rrichter@marvell.com --- drivers/edac/edac_mc.c | 67 +++++++++--------------------------- drivers/edac/edac_mc_sysfs.c | 20 +++++------ include/linux/edac.h | 4 ++- 3 files changed, 27 insertions(+), 64 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 80629c702a4f..75ede27bdf6a 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -451,11 +451,9 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, { struct mem_ctl_info *mci; struct edac_mc_layer *layer; - u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; - unsigned int idx, size, tot_dimms = 1, count = 1; - unsigned int tot_csrows = 1, tot_channels = 1, tot_errcount = 0; + unsigned int idx, size, tot_dimms = 1; + unsigned int tot_csrows = 1, tot_channels = 1; void *pvt, *ptr = NULL; - int i; bool per_rank = false; if (WARN_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0)) @@ -482,19 +480,10 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, * stringent as what the compiler would provide if we could simply * hardcode everything into a single struct. */ - mci = edac_align_ptr(&ptr, sizeof(*mci), 1); - layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); - for (i = 0; i < n_layers; i++) { - count *= layers[i].size; - edac_dbg(4, "errcount layer %d size %d\n", i, count); - ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); - ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); - tot_errcount += 2 * count; - } - - edac_dbg(4, "allocating %d error counters\n", tot_errcount); - pvt = edac_align_ptr(&ptr, sz_pvt, 1); - size = ((unsigned long)pvt) + sz_pvt; + mci = edac_align_ptr(&ptr, sizeof(*mci), 1); + layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); + pvt = edac_align_ptr(&ptr, sz_pvt, 1); + size = ((unsigned long)pvt) + sz_pvt; edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n", size, @@ -513,10 +502,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, * rather than an imaginary chunk of memory located at address 0. */ layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer)); - for (i = 0; i < n_layers; i++) { - mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i])); - mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i])); - } pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL; /* setup index and various internal pointers */ @@ -949,48 +934,28 @@ static void edac_inc_ce_error(struct edac_raw_error_desc *e) { int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; struct mem_ctl_info *mci = error_desc_to_mci(e); - int i, index = 0; + struct dimm_info *dimm = edac_get_dimm(mci, pos[0], pos[1], pos[2]); mci->ce_mc += e->error_count; - if (pos[0] < 0) { + if (dimm) + dimm->ce_count += e->error_count; + else mci->ce_noinfo_count += e->error_count; - return; - } - - for (i = 0; i < mci->n_layers; i++) { - if (pos[i] < 0) - break; - index += pos[i]; - mci->ce_per_layer[i][index] += e->error_count; - - if (i < mci->n_layers - 1) - index *= mci->layers[i + 1].size; - } } static void edac_inc_ue_error(struct edac_raw_error_desc *e) { int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; struct mem_ctl_info *mci = error_desc_to_mci(e); - int i, index = 0; + struct dimm_info *dimm = edac_get_dimm(mci, pos[0], pos[1], pos[2]); mci->ue_mc += e->error_count; - if (pos[0] < 0) { + if (dimm) + dimm->ue_count += e->error_count; + else mci->ue_noinfo_count += e->error_count; - return; - } - - for (i = 0; i < mci->n_layers; i++) { - if (pos[i] < 0) - break; - index += pos[i]; - mci->ue_per_layer[i][index] += e->error_count; - - if (i < mci->n_layers - 1) - index *= mci->layers[i + 1].size; - } } static void edac_ce_error(struct edac_raw_error_desc *e) @@ -1143,8 +1108,8 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, /* * Check if the event report is consistent and if the memory location is - * known. If it is, the DIMM(s) label info will be filled and the - * per-layer error counters will be incremented. + * known. If it is, the DIMM(s) label info will be filled and the DIMM's + * error counters will be incremented. */ for (i = 0; i < mci->n_layers; i++) { if (pos[i] >= (int)mci->layers[i].size) { diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 11e1b436f116..4e6aca595133 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -551,10 +551,8 @@ static ssize_t dimmdev_ce_count_show(struct device *dev, char *data) { struct dimm_info *dimm = to_dimm(dev); - u32 count; - count = dimm->mci->ce_per_layer[dimm->mci->n_layers-1][dimm->idx]; - return sprintf(data, "%u\n", count); + return sprintf(data, "%u\n", dimm->ce_count); } static ssize_t dimmdev_ue_count_show(struct device *dev, @@ -562,10 +560,8 @@ static ssize_t dimmdev_ue_count_show(struct device *dev, char *data) { struct dimm_info *dimm = to_dimm(dev); - u32 count; - count = dimm->mci->ue_per_layer[dimm->mci->n_layers-1][dimm->idx]; - return sprintf(data, "%u\n", count); + return sprintf(data, "%u\n", dimm->ue_count); } /* dimm/rank attribute files */ @@ -661,7 +657,9 @@ static ssize_t mci_reset_counters_store(struct device *dev, const char *data, size_t count) { struct mem_ctl_info *mci = to_mci(dev); - int cnt, row, chan, i; + struct dimm_info *dimm; + int row, chan; + mci->ue_mc = 0; mci->ce_mc = 0; mci->ue_noinfo_count = 0; @@ -677,11 +675,9 @@ static ssize_t mci_reset_counters_store(struct device *dev, ri->channels[chan]->ce_count = 0; } - cnt = 1; - for (i = 0; i < mci->n_layers; i++) { - cnt *= mci->layers[i].size; - memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32)); - memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32)); + mci_for_each_dimm(mci, dimm) { + dimm->ue_count = 0; + dimm->ce_count = 0; } mci->start_time = jiffies; diff --git a/include/linux/edac.h b/include/linux/edac.h index 815f246e0abd..0f20b986b0ab 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -383,6 +383,9 @@ struct dimm_info { unsigned int csrow, cschannel; /* Points to the old API data */ u16 smbios_handle; /* Handle for SMBIOS type 17 */ + + u32 ce_count; + u32 ue_count; }; /** @@ -559,7 +562,6 @@ struct mem_ctl_info { */ u32 ce_noinfo_count, ue_noinfo_count; u32 ue_mc, ce_mc; - u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; struct completion complete; From 50e818715821b89c7abac90a97721f106e893d83 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:48 +0100 Subject: [PATCH 0280/2231] x86/vdso: Mark the TSC clocksource path likely Jumping out of line for the TSC clcoksource read is creating awful code. TSC is likely to be the clocksource at least on bare metal and the PV interfaces are sufficiently more work that the jump over the TSC read is just in the noise. Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124402.328922847@linutronix.de --- arch/x86/include/asm/vdso/gettimeofday.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/vdso/gettimeofday.h b/arch/x86/include/asm/vdso/gettimeofday.h index 6ee1f7dba34b..264d4fd3ff2c 100644 --- a/arch/x86/include/asm/vdso/gettimeofday.h +++ b/arch/x86/include/asm/vdso/gettimeofday.h @@ -243,7 +243,7 @@ static u64 vread_hvclock(void) static inline u64 __arch_get_hw_counter(s32 clock_mode) { - if (clock_mode == VCLOCK_TSC) + if (likely(clock_mode == VCLOCK_TSC)) return (u64)rdtsc_ordered(); /* * For any memory-mapped vclock type, we need to make sure that gcc From 78560d41064ad3d377e3d1a1ee87526301f4e946 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:49 +0100 Subject: [PATCH 0281/2231] ARM: vdso: Remove unused function The function is nowhere used. Aside of that this check should only cover the high resolution parts of the VDSO which require a VDSO capable clocksource and not the complete functionality as the name suggests. Will be replaced with something more useful. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124402.438179009@linutronix.de --- arch/arm/include/asm/vdso/vsyscall.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/arm/include/asm/vdso/vsyscall.h b/arch/arm/include/asm/vdso/vsyscall.h index cff87d8d30da..85a7e58b7228 100644 --- a/arch/arm/include/asm/vdso/vsyscall.h +++ b/arch/arm/include/asm/vdso/vsyscall.h @@ -49,13 +49,6 @@ int __arm_get_clock_mode(struct timekeeper *tk) } #define __arch_get_clock_mode __arm_get_clock_mode -static __always_inline -int __arm_use_vsyscall(struct vdso_data *vdata) -{ - return vdata[CS_HRES_COARSE].clock_mode; -} -#define __arch_use_vsyscall __arm_use_vsyscall - static __always_inline void __arm_sync_vdso_data(struct vdso_data *vdata) { From 1dff4156d1f63b525c54aea7f097a657cbbbf837 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:50 +0100 Subject: [PATCH 0282/2231] lib/vdso: Allow the high resolution parts to be compiled out If the architecture knows at compile time that there is no VDSO capable clocksource supported it makes sense to optimize the guts of the high resolution parts of the VDSO out at build time. Add a helper function to check whether the VDSO should be high resolution capable and provide a stub which can be overridden by an architecture. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124402.530143168@linutronix.de --- lib/vdso/gettimeofday.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index f8b8ec5e63ac..5804e4e168e7 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -38,6 +38,13 @@ u64 vdso_calc_delta(u64 cycles, u64 last, u64 mask, u32 mult) } #endif +#ifndef __arch_vdso_hres_capable +static inline bool __arch_vdso_hres_capable(void) +{ + return true; +} +#endif + #ifdef CONFIG_TIME_NS static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk, struct __kernel_timespec *ts) @@ -101,6 +108,10 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, u64 cycles, last, sec, ns; u32 seq; + /* Allows to compile the high resolution parts out */ + if (!__arch_vdso_hres_capable()) + return -1; + do { /* * Open coded to handle VCLOCK_TIMENS. Time namespace From 3280badbe1b289622ce12b94d064ddb624cbaef1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:51 +0100 Subject: [PATCH 0283/2231] ARM: vdso: Compile high resolution parts conditionally If the architected timer is disabled in the kernel configuration then let the core VDSO code drop the high resolution parts at compile time. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124402.622587341@linutronix.de --- arch/arm/include/asm/vdso/gettimeofday.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/include/asm/vdso/gettimeofday.h b/arch/arm/include/asm/vdso/gettimeofday.h index fe6e1f65932d..f4757d327f43 100644 --- a/arch/arm/include/asm/vdso/gettimeofday.h +++ b/arch/arm/include/asm/vdso/gettimeofday.h @@ -106,6 +106,12 @@ static __always_inline int clock_getres32_fallback( return ret; } +static inline bool arm_vdso_hres_capable(void) +{ + return IS_ENABLED(CONFIG_ARM_ARCH_TIMER); +} +#define __arch_vdso_hres_capable arm_vdso_hres_capable + static __always_inline u64 __arch_get_hw_counter(int clock_mode) { #ifdef CONFIG_ARM_ARCH_TIMER From 25a2a6567829119f5e3e11eb0ce3d8ae985b6019 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:52 +0100 Subject: [PATCH 0284/2231] MIPS: vdso: Compile high resolution parts conditionally If neither the R4K nor the GIC timer is enabled in the kernel configuration then let the core VDSO code drop the high resolution parts at compile time. Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124402.714585315@linutronix.de --- arch/mips/include/asm/vdso/gettimeofday.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/mips/include/asm/vdso/gettimeofday.h b/arch/mips/include/asm/vdso/gettimeofday.h index a58687e26c5d..a9f846b1a920 100644 --- a/arch/mips/include/asm/vdso/gettimeofday.h +++ b/arch/mips/include/asm/vdso/gettimeofday.h @@ -199,6 +199,13 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode) return cycle_now; } +static inline bool mips_vdso_hres_capable(void) +{ + return IS_ENABLED(CONFIG_CSRC_R4K) || + IS_ENABLED(CONFIG_CLKSRC_MIPS_GIC); +} +#define __arch_vdso_hres_capable mips_vdso_hres_capable + static __always_inline const struct vdso_data *__arch_get_vdso_data(void) { return get_vdso_data(); From 3bd142a46b561a12408e8db78cc6d62eb1c6b84e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:53 +0100 Subject: [PATCH 0285/2231] clocksource: Cleanup struct clocksource and documentation Reformat the struct definition, add missing member documentation. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124402.825471920@linutronix.de --- include/linux/clocksource.h | 87 ++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index b21db536fd52..2c4574b517d2 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -32,9 +32,19 @@ struct module; * Provides mostly state-free accessors to the underlying hardware. * This is the structure used for system time. * - * @name: ptr to clocksource name - * @list: list head for registration - * @rating: rating value for selection (higher is better) + * @read: Returns a cycle value, passes clocksource as argument + * @mask: Bitmask for two's complement + * subtraction of non 64 bit counters + * @mult: Cycle to nanosecond multiplier + * @shift: Cycle to nanosecond divisor (power of two) + * @max_idle_ns: Maximum idle time permitted by the clocksource (nsecs) + * @maxadj: Maximum adjustment value to mult (~11%) + * @archdata: Optional arch-specific data + * @max_cycles: Maximum safe cycle value which won't overflow on + * multiplication + * @name: Pointer to clocksource name + * @list: List head for registration (internal) + * @rating: Rating value for selection (higher is better) * To avoid rating inflation the following * list should give you a guide as to how * to assign your clocksource a rating @@ -49,27 +59,23 @@ struct module; * 400-499: Perfect * The ideal clocksource. A must-use where * available. - * @read: returns a cycle value, passes clocksource as argument - * @enable: optional function to enable the clocksource - * @disable: optional function to disable the clocksource - * @mask: bitmask for two's complement - * subtraction of non 64 bit counters - * @mult: cycle to nanosecond multiplier - * @shift: cycle to nanosecond divisor (power of two) - * @max_idle_ns: max idle time permitted by the clocksource (nsecs) - * @maxadj: maximum adjustment value to mult (~11%) - * @max_cycles: maximum safe cycle value which won't overflow on multiplication - * @flags: flags describing special properties - * @archdata: arch-specific data - * @suspend: suspend function for the clocksource, if necessary - * @resume: resume function for the clocksource, if necessary + * @flags: Flags describing special properties + * @enable: Optional function to enable the clocksource + * @disable: Optional function to disable the clocksource + * @suspend: Optional suspend function for the clocksource + * @resume: Optional resume function for the clocksource * @mark_unstable: Optional function to inform the clocksource driver that * the watchdog marked the clocksource unstable - * @owner: module reference, must be set by clocksource in modules + * @tick_stable: Optional function called periodically from the watchdog + * code to provide stable syncrhonization points + * @wd_list: List head to enqueue into the watchdog list (internal) + * @cs_last: Last clocksource value for clocksource watchdog + * @wd_last: Last watchdog value corresponding to @cs_last + * @owner: Module reference, must be set by clocksource in modules * * Note: This struct is not used in hotpathes of the timekeeping code * because the timekeeper caches the hot path fields in its own data - * structure, so no line cache alignment is required, + * structure, so no cache line alignment is required, * * The pointer to the clocksource itself is handed to the read * callback. If you need extra information there you can wrap struct @@ -78,35 +84,36 @@ struct module; * structure. */ struct clocksource { - u64 (*read)(struct clocksource *cs); - u64 mask; - u32 mult; - u32 shift; - u64 max_idle_ns; - u32 maxadj; + u64 (*read)(struct clocksource *cs); + u64 mask; + u32 mult; + u32 shift; + u64 max_idle_ns; + u32 maxadj; #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA struct arch_clocksource_data archdata; #endif - u64 max_cycles; - const char *name; - struct list_head list; - int rating; - int (*enable)(struct clocksource *cs); - void (*disable)(struct clocksource *cs); - unsigned long flags; - void (*suspend)(struct clocksource *cs); - void (*resume)(struct clocksource *cs); - void (*mark_unstable)(struct clocksource *cs); - void (*tick_stable)(struct clocksource *cs); + u64 max_cycles; + const char *name; + struct list_head list; + int rating; + unsigned long flags; + + int (*enable)(struct clocksource *cs); + void (*disable)(struct clocksource *cs); + void (*suspend)(struct clocksource *cs); + void (*resume)(struct clocksource *cs); + void (*mark_unstable)(struct clocksource *cs); + void (*tick_stable)(struct clocksource *cs); /* private: */ #ifdef CONFIG_CLOCKSOURCE_WATCHDOG /* Watchdog related data, used by the framework */ - struct list_head wd_list; - u64 cs_last; - u64 wd_last; + struct list_head wd_list; + u64 cs_last; + u64 wd_last; #endif - struct module *owner; + struct module *owner; }; /* From eec399dd862762b9594df3659f15839a4e12f17a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:54 +0100 Subject: [PATCH 0286/2231] x86/vdso: Move VDSO clocksource state tracking to callback All architectures which use the generic VDSO code have their own storage for the VDSO clock mode. That's pointless and just requires duplicate code. X86 abuses the function which retrieves the architecture specific clock mode storage to mark the clocksource as used in the VDSO. That's silly because this is invoked on every tick when the VDSO data is updated. Move this functionality to the clocksource::enable() callback so it gets invoked once when the clocksource is installed. This allows to make the clock mode storage generic. Signed-off-by: Thomas Gleixner Reviewed-by: Michael Kelley (Hyper-V parts) Reviewed-by: Vincenzo Frascino (VDSO parts) Acked-by: Juergen Gross (Xen parts) Link: https://lkml.kernel.org/r/20200207124402.934519777@linutronix.de --- arch/x86/entry/vdso/vma.c | 4 ++++ arch/x86/include/asm/clocksource.h | 12 +++++++++++ arch/x86/include/asm/mshyperv.h | 2 ++ arch/x86/include/asm/vdso/vsyscall.h | 10 +-------- arch/x86/include/asm/vgtod.h | 6 ------ arch/x86/kernel/kvmclock.c | 7 ++++++ arch/x86/kernel/tsc.c | 32 +++++++++++++++++----------- arch/x86/xen/time.c | 17 ++++++++++----- drivers/clocksource/hyperv_timer.c | 7 ++++++ 9 files changed, 65 insertions(+), 32 deletions(-) diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c index c1b8496b5606..cce3e809f17e 100644 --- a/arch/x86/entry/vdso/vma.c +++ b/arch/x86/entry/vdso/vma.c @@ -38,6 +38,8 @@ struct vdso_data *arch_get_vdso_data(void *vvar_page) } #undef EMIT_VVAR +unsigned int vclocks_used __read_mostly; + #if defined(CONFIG_X86_64) unsigned int __read_mostly vdso64_enabled = 1; #endif @@ -445,6 +447,8 @@ __setup("vdso=", vdso_setup); static int __init init_vdso(void) { + BUILD_BUG_ON(VCLOCK_MAX >= 32); + init_vdso_image(&vdso_image_64); #ifdef CONFIG_X86_X32_ABI diff --git a/arch/x86/include/asm/clocksource.h b/arch/x86/include/asm/clocksource.h index dc4cfc888d6d..2450d6e02a5d 100644 --- a/arch/x86/include/asm/clocksource.h +++ b/arch/x86/include/asm/clocksource.h @@ -14,4 +14,16 @@ struct arch_clocksource_data { int vclock_mode; }; +extern unsigned int vclocks_used; + +static inline bool vclock_was_used(int vclock) +{ + return READ_ONCE(vclocks_used) & (1U << vclock); +} + +static inline void vclocks_set_used(unsigned int which) +{ + WRITE_ONCE(vclocks_used, READ_ONCE(vclocks_used) | (1 << which)); +} + #endif /* _ASM_X86_CLOCKSOURCE_H */ diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 6b79515abb82..f7cbc01f128e 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -47,6 +47,8 @@ typedef int (*hyperv_fill_flush_list_func)( wrmsrl(HV_X64_MSR_REFERENCE_TSC, val) #define hv_set_clocksource_vdso(val) \ ((val).archdata.vclock_mode = VCLOCK_HVCLOCK) +#define hv_enable_vdso_clocksource() \ + vclocks_set_used(VCLOCK_HVCLOCK); #define hv_get_raw_timer() rdtsc_ordered() void hyperv_callback_vector(void); diff --git a/arch/x86/include/asm/vdso/vsyscall.h b/arch/x86/include/asm/vdso/vsyscall.h index 0026ab2123ce..01f5733d14d6 100644 --- a/arch/x86/include/asm/vdso/vsyscall.h +++ b/arch/x86/include/asm/vdso/vsyscall.h @@ -10,8 +10,6 @@ #include #include -int vclocks_used __read_mostly; - DEFINE_VVAR(struct vdso_data, _vdso_data); /* * Update the vDSO data page to keep in sync with kernel timekeeping. @@ -26,13 +24,7 @@ struct vdso_data *__x86_get_k_vdso_data(void) static __always_inline int __x86_get_clock_mode(struct timekeeper *tk) { - int vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode; - - /* Mark the new vclock used. */ - BUILD_BUG_ON(VCLOCK_MAX >= 32); - WRITE_ONCE(vclocks_used, READ_ONCE(vclocks_used) | (1 << vclock_mode)); - - return vclock_mode; + return tk->tkr_mono.clock->archdata.vclock_mode; } #define __arch_get_clock_mode __x86_get_clock_mode diff --git a/arch/x86/include/asm/vgtod.h b/arch/x86/include/asm/vgtod.h index a2638c6124ed..fc8e4cd342cc 100644 --- a/arch/x86/include/asm/vgtod.h +++ b/arch/x86/include/asm/vgtod.h @@ -15,10 +15,4 @@ typedef u64 gtod_long_t; typedef unsigned long gtod_long_t; #endif -extern int vclocks_used; -static inline bool vclock_was_used(int vclock) -{ - return READ_ONCE(vclocks_used) & (1 << vclock); -} - #endif /* _ASM_X86_VGTOD_H */ diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index 904494b924c1..33f2cac25f13 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c @@ -159,12 +159,19 @@ bool kvm_check_and_clear_guest_paused(void) return ret; } +static int kvm_cs_enable(struct clocksource *cs) +{ + vclocks_set_used(VCLOCK_PVCLOCK); + return 0; +} + struct clocksource kvm_clock = { .name = "kvm-clock", .read = kvm_clock_get_cycles, .rating = 400, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .enable = kvm_cs_enable, }; EXPORT_SYMBOL_GPL(kvm_clock); diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 7e322e2daaf5..742da141a30a 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -1108,17 +1108,24 @@ static void tsc_cs_tick_stable(struct clocksource *cs) sched_clock_tick_stable(); } +static int tsc_cs_enable(struct clocksource *cs) +{ + vclocks_set_used(VCLOCK_TSC); + return 0; +} + /* * .mask MUST be CLOCKSOURCE_MASK(64). See comment above read_tsc() */ static struct clocksource clocksource_tsc_early = { - .name = "tsc-early", - .rating = 299, - .read = read_tsc, - .mask = CLOCKSOURCE_MASK(64), - .flags = CLOCK_SOURCE_IS_CONTINUOUS | + .name = "tsc-early", + .rating = 299, + .read = read_tsc, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_MUST_VERIFY, - .archdata = { .vclock_mode = VCLOCK_TSC }, + .archdata = { .vclock_mode = VCLOCK_TSC }, + .enable = tsc_cs_enable, .resume = tsc_resume, .mark_unstable = tsc_cs_mark_unstable, .tick_stable = tsc_cs_tick_stable, @@ -1131,14 +1138,15 @@ static struct clocksource clocksource_tsc_early = { * been found good. */ static struct clocksource clocksource_tsc = { - .name = "tsc", - .rating = 300, - .read = read_tsc, - .mask = CLOCKSOURCE_MASK(64), - .flags = CLOCK_SOURCE_IS_CONTINUOUS | + .name = "tsc", + .rating = 300, + .read = read_tsc, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_MUST_VERIFY, - .archdata = { .vclock_mode = VCLOCK_TSC }, + .archdata = { .vclock_mode = VCLOCK_TSC }, + .enable = tsc_cs_enable, .resume = tsc_resume, .mark_unstable = tsc_cs_mark_unstable, .tick_stable = tsc_cs_tick_stable, diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index befbdd8b17f0..5d1568ff19ea 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -145,12 +145,19 @@ static struct notifier_block xen_pvclock_gtod_notifier = { .notifier_call = xen_pvclock_gtod_notify, }; +static int xen_cs_enable(struct clocksource *cs) +{ + vclocks_set_used(VCLOCK_PVCLOCK); + return 0; +} + static struct clocksource xen_clocksource __read_mostly = { - .name = "xen", - .rating = 400, - .read = xen_clocksource_get_cycles, - .mask = ~0, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .name = "xen", + .rating = 400, + .read = xen_clocksource_get_cycles, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .enable = xen_cs_enable, }; /* diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index 9d808d595ca8..a86859ccc61b 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -369,6 +369,12 @@ static void resume_hv_clock_tsc(struct clocksource *arg) hv_set_reference_tsc(tsc_msr); } +static int hv_cs_enable(struct clocksource *cs) +{ + hv_enable_vdso_clocksource(); + return 0; +} + static struct clocksource hyperv_cs_tsc = { .name = "hyperv_clocksource_tsc_page", .rating = 250, @@ -377,6 +383,7 @@ static struct clocksource hyperv_cs_tsc = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, .suspend= suspend_hv_clock_tsc, .resume = resume_hv_clock_tsc, + .enable = hv_cs_enable, }; static u64 notrace read_hv_clock_msr(void) From 5d51bee725cc1497352d6b0b604e42a90c680540 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:55 +0100 Subject: [PATCH 0287/2231] clocksource: Add common vdso clock mode storage All architectures which use the generic VDSO code have their own storage for the VDSO clock mode. That's pointless and just requires duplicate code. Provide generic storage for it. The new Kconfig symbol is intermediate and will be removed once all architectures are converted over. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.028046322@linutronix.de --- include/linux/clocksource.h | 12 +++++++++++- kernel/time/clocksource.c | 9 +++++++++ kernel/time/vsyscall.c | 10 ++++++++-- lib/vdso/Kconfig | 3 +++ lib/vdso/gettimeofday.c | 13 +++++++++++-- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 2c4574b517d2..6d5ed1b4d24d 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -23,10 +23,19 @@ struct clocksource; struct module; -#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA +#if defined(CONFIG_ARCH_CLOCKSOURCE_DATA) || \ + defined(CONFIG_GENERIC_VDSO_CLOCK_MODE) #include #endif +enum vdso_clock_mode { + VDSO_CLOCKMODE_NONE, +#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE + VDSO_ARCH_CLOCKMODES, +#endif + VDSO_CLOCKMODE_MAX, +}; + /** * struct clocksource - hardware abstraction for a free running counter * Provides mostly state-free accessors to the underlying hardware. @@ -97,6 +106,7 @@ struct clocksource { const char *name; struct list_head list; int rating; + enum vdso_clock_mode vdso_clock_mode; unsigned long flags; int (*enable)(struct clocksource *cs); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 428beb69426a..7cb09c4cf21c 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -928,6 +928,15 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq) clocksource_arch_init(cs); +#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE + if (cs->vdso_clock_mode < 0 || + cs->vdso_clock_mode >= VDSO_CLOCKMODE_MAX) { + pr_warn("clocksource %s registered with invalid VDSO mode %d. Disabling VDSO support.\n", + cs->name, cs->vdso_clock_mode); + cs->vdso_clock_mode = VDSO_CLOCKMODE_NONE; + } +#endif + /* Initialize mult/shift and max_idle_ns */ __clocksource_update_freq_scale(cs, scale, freq); diff --git a/kernel/time/vsyscall.c b/kernel/time/vsyscall.c index 9577c89179cd..f9a5178c69bb 100644 --- a/kernel/time/vsyscall.c +++ b/kernel/time/vsyscall.c @@ -71,13 +71,19 @@ void update_vsyscall(struct timekeeper *tk) { struct vdso_data *vdata = __arch_get_k_vdso_data(); struct vdso_timestamp *vdso_ts; + s32 clock_mode; u64 nsec; /* copy vsyscall data */ vdso_write_begin(vdata); - vdata[CS_HRES_COARSE].clock_mode = __arch_get_clock_mode(tk); - vdata[CS_RAW].clock_mode = __arch_get_clock_mode(tk); +#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE + clock_mode = tk->tkr_mono.clock->vdso_clock_mode; +#else + clock_mode = __arch_get_clock_mode(tk); +#endif + vdata[CS_HRES_COARSE].clock_mode = clock_mode; + vdata[CS_RAW].clock_mode = clock_mode; /* CLOCK_REALTIME also required for time() */ vdso_ts = &vdata[CS_HRES_COARSE].basetime[CLOCK_REALTIME]; diff --git a/lib/vdso/Kconfig b/lib/vdso/Kconfig index d883ac299508..d9f43c84fcc6 100644 --- a/lib/vdso/Kconfig +++ b/lib/vdso/Kconfig @@ -30,4 +30,7 @@ config GENERIC_VDSO_TIME_NS Selected by architectures which support time namespaces in the VDSO +config GENERIC_VDSO_CLOCK_MODE + bool + endif diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index 5804e4e168e7..3f2d8b859130 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -64,10 +65,14 @@ static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk, do { seq = vdso_read_begin(vd); + if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && + vd->clock_mode == VDSO_CLOCKMODE_NONE) + return -1; cycles = __arch_get_hw_counter(vd->clock_mode); ns = vdso_ts->nsec; last = vd->cycle_last; - if (unlikely((s64)cycles < 0)) + if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && + unlikely((s64)cycles < 0)) return -1; ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); @@ -132,10 +137,14 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, } smp_rmb(); + if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && + vd->clock_mode == VDSO_CLOCKMODE_NONE) + return -1; cycles = __arch_get_hw_counter(vd->clock_mode); ns = vdso_ts->nsec; last = vd->cycle_last; - if (unlikely((s64)cycles < 0)) + if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && + unlikely((s64)cycles < 0)) return -1; ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); From b95a8a27c300d1a39a4e36f63a518ef36e4b966c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:56 +0100 Subject: [PATCH 0288/2231] x86/vdso: Use generic VDSO clock mode storage Switch to the generic VDSO clock mode storage. Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino (VDSO parts) Acked-by: Juergen Gross (Xen parts) Acked-by: Paolo Bonzini (KVM parts) Link: https://lkml.kernel.org/r/20200207124403.152039903@linutronix.de --- arch/x86/Kconfig | 2 +- arch/x86/entry/vdso/vma.c | 6 +++--- arch/x86/include/asm/clocksource.h | 13 ++++--------- arch/x86/include/asm/mshyperv.h | 4 ++-- arch/x86/include/asm/vdso/gettimeofday.h | 6 +++--- arch/x86/include/asm/vdso/vsyscall.h | 7 ------- arch/x86/kernel/kvmclock.c | 4 ++-- arch/x86/kernel/pvclock.c | 2 +- arch/x86/kernel/time.c | 12 +++--------- arch/x86/kernel/tsc.c | 6 +++--- arch/x86/kvm/trace.h | 4 ++-- arch/x86/kvm/x86.c | 22 +++++++++++----------- arch/x86/xen/time.c | 21 +++++++++++---------- 13 files changed, 46 insertions(+), 63 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index beea77046f9b..698e9c835cc5 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -57,7 +57,6 @@ config X86 select ACPI_LEGACY_TABLES_LOOKUP if ACPI select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI select ARCH_32BIT_OFF_T if X86_32 - select ARCH_CLOCKSOURCE_DATA select ARCH_CLOCKSOURCE_INIT select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI select ARCH_HAS_DEBUG_VIRTUAL @@ -126,6 +125,7 @@ config X86 select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select GENERIC_GETTIMEOFDAY + select GENERIC_VDSO_CLOCK_MODE select GENERIC_VDSO_TIME_NS select GUP_GET_PTE_LOW_HIGH if X86_PAE select HARDLOCKUP_CHECK_TIMESTAMP if X86_64 diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c index cce3e809f17e..43428cc514c8 100644 --- a/arch/x86/entry/vdso/vma.c +++ b/arch/x86/entry/vdso/vma.c @@ -221,7 +221,7 @@ static vm_fault_t vvar_fault(const struct vm_special_mapping *sm, } else if (sym_offset == image->sym_pvclock_page) { struct pvclock_vsyscall_time_info *pvti = pvclock_get_pvti_cpu0_va(); - if (pvti && vclock_was_used(VCLOCK_PVCLOCK)) { + if (pvti && vclock_was_used(VDSO_CLOCKMODE_PVCLOCK)) { return vmf_insert_pfn_prot(vma, vmf->address, __pa(pvti) >> PAGE_SHIFT, pgprot_decrypted(vma->vm_page_prot)); @@ -229,7 +229,7 @@ static vm_fault_t vvar_fault(const struct vm_special_mapping *sm, } else if (sym_offset == image->sym_hvclock_page) { struct ms_hyperv_tsc_page *tsc_pg = hv_get_tsc_page(); - if (tsc_pg && vclock_was_used(VCLOCK_HVCLOCK)) + if (tsc_pg && vclock_was_used(VDSO_CLOCKMODE_HVCLOCK)) return vmf_insert_pfn(vma, vmf->address, virt_to_phys(tsc_pg) >> PAGE_SHIFT); } else if (sym_offset == image->sym_timens_page) { @@ -447,7 +447,7 @@ __setup("vdso=", vdso_setup); static int __init init_vdso(void) { - BUILD_BUG_ON(VCLOCK_MAX >= 32); + BUILD_BUG_ON(VDSO_CLOCKMODE_MAX >= 32); init_vdso_image(&vdso_image_64); diff --git a/arch/x86/include/asm/clocksource.h b/arch/x86/include/asm/clocksource.h index 2450d6e02a5d..d561db67f96d 100644 --- a/arch/x86/include/asm/clocksource.h +++ b/arch/x86/include/asm/clocksource.h @@ -4,15 +4,10 @@ #ifndef _ASM_X86_CLOCKSOURCE_H #define _ASM_X86_CLOCKSOURCE_H -#define VCLOCK_NONE 0 /* No vDSO clock available. */ -#define VCLOCK_TSC 1 /* vDSO should use vread_tsc. */ -#define VCLOCK_PVCLOCK 2 /* vDSO should use vread_pvclock. */ -#define VCLOCK_HVCLOCK 3 /* vDSO should use vread_hvclock. */ -#define VCLOCK_MAX 3 - -struct arch_clocksource_data { - int vclock_mode; -}; +#define VDSO_ARCH_CLOCKMODES \ + VDSO_CLOCKMODE_TSC, \ + VDSO_CLOCKMODE_PVCLOCK, \ + VDSO_CLOCKMODE_HVCLOCK extern unsigned int vclocks_used; diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index f7cbc01f128e..edc2c581704a 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -46,9 +46,9 @@ typedef int (*hyperv_fill_flush_list_func)( #define hv_set_reference_tsc(val) \ wrmsrl(HV_X64_MSR_REFERENCE_TSC, val) #define hv_set_clocksource_vdso(val) \ - ((val).archdata.vclock_mode = VCLOCK_HVCLOCK) + ((val).vdso_clock_mode = VDSO_CLOCKMODE_HVCLOCK) #define hv_enable_vdso_clocksource() \ - vclocks_set_used(VCLOCK_HVCLOCK); + vclocks_set_used(VDSO_CLOCKMODE_HVCLOCK); #define hv_get_raw_timer() rdtsc_ordered() void hyperv_callback_vector(void); diff --git a/arch/x86/include/asm/vdso/gettimeofday.h b/arch/x86/include/asm/vdso/gettimeofday.h index 264d4fd3ff2c..9a6dc9b4ec99 100644 --- a/arch/x86/include/asm/vdso/gettimeofday.h +++ b/arch/x86/include/asm/vdso/gettimeofday.h @@ -243,7 +243,7 @@ static u64 vread_hvclock(void) static inline u64 __arch_get_hw_counter(s32 clock_mode) { - if (likely(clock_mode == VCLOCK_TSC)) + if (likely(clock_mode == VDSO_CLOCKMODE_TSC)) return (u64)rdtsc_ordered(); /* * For any memory-mapped vclock type, we need to make sure that gcc @@ -252,13 +252,13 @@ static inline u64 __arch_get_hw_counter(s32 clock_mode) * question isn't enabled, which will segfault. Hence the barriers. */ #ifdef CONFIG_PARAVIRT_CLOCK - if (clock_mode == VCLOCK_PVCLOCK) { + if (clock_mode == VDSO_CLOCKMODE_PVCLOCK) { barrier(); return vread_pvclock(); } #endif #ifdef CONFIG_HYPERV_TIMER - if (clock_mode == VCLOCK_HVCLOCK) { + if (clock_mode == VDSO_CLOCKMODE_HVCLOCK) { barrier(); return vread_hvclock(); } diff --git a/arch/x86/include/asm/vdso/vsyscall.h b/arch/x86/include/asm/vdso/vsyscall.h index 01f5733d14d6..be199a9b2676 100644 --- a/arch/x86/include/asm/vdso/vsyscall.h +++ b/arch/x86/include/asm/vdso/vsyscall.h @@ -21,13 +21,6 @@ struct vdso_data *__x86_get_k_vdso_data(void) } #define __arch_get_k_vdso_data __x86_get_k_vdso_data -static __always_inline -int __x86_get_clock_mode(struct timekeeper *tk) -{ - return tk->tkr_mono.clock->archdata.vclock_mode; -} -#define __arch_get_clock_mode __x86_get_clock_mode - /* The asm-generic header needs to be included after the definitions above */ #include diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index 33f2cac25f13..34b18f6eeb2c 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c @@ -161,7 +161,7 @@ bool kvm_check_and_clear_guest_paused(void) static int kvm_cs_enable(struct clocksource *cs) { - vclocks_set_used(VCLOCK_PVCLOCK); + vclocks_set_used(VDSO_CLOCKMODE_PVCLOCK); return 0; } @@ -279,7 +279,7 @@ static int __init kvm_setup_vsyscall_timeinfo(void) if (!(flags & PVCLOCK_TSC_STABLE_BIT)) return 0; - kvm_clock.archdata.vclock_mode = VCLOCK_PVCLOCK; + kvm_clock.vdso_clock_mode = VDSO_CLOCKMODE_PVCLOCK; #endif kvmclock_init_mem(); diff --git a/arch/x86/kernel/pvclock.c b/arch/x86/kernel/pvclock.c index 10125358b9c4..11065dc03f5b 100644 --- a/arch/x86/kernel/pvclock.c +++ b/arch/x86/kernel/pvclock.c @@ -145,7 +145,7 @@ void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock, void pvclock_set_pvti_cpu0_va(struct pvclock_vsyscall_time_info *pvti) { - WARN_ON(vclock_was_used(VCLOCK_PVCLOCK)); + WARN_ON(vclock_was_used(VDSO_CLOCKMODE_PVCLOCK)); pvti_cpu0_va = pvti; } diff --git a/arch/x86/kernel/time.c b/arch/x86/kernel/time.c index d8673d8a779b..4d545db52efc 100644 --- a/arch/x86/kernel/time.c +++ b/arch/x86/kernel/time.c @@ -122,18 +122,12 @@ void __init time_init(void) */ void clocksource_arch_init(struct clocksource *cs) { - if (cs->archdata.vclock_mode == VCLOCK_NONE) + if (cs->vdso_clock_mode == VDSO_CLOCKMODE_NONE) return; - if (cs->archdata.vclock_mode > VCLOCK_MAX) { - pr_warn("clocksource %s registered with invalid vclock_mode %d. Disabling vclock.\n", - cs->name, cs->archdata.vclock_mode); - cs->archdata.vclock_mode = VCLOCK_NONE; - } - if (cs->mask != CLOCKSOURCE_MASK(64)) { - pr_warn("clocksource %s registered with invalid mask %016llx. Disabling vclock.\n", + pr_warn("clocksource %s registered with invalid mask %016llx for VDSO. Disabling VDSO support.\n", cs->name, cs->mask); - cs->archdata.vclock_mode = VCLOCK_NONE; + cs->vdso_clock_mode = VDSO_CLOCKMODE_NONE; } } diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 742da141a30a..971d6f0216df 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -1110,7 +1110,7 @@ static void tsc_cs_tick_stable(struct clocksource *cs) static int tsc_cs_enable(struct clocksource *cs) { - vclocks_set_used(VCLOCK_TSC); + vclocks_set_used(VDSO_CLOCKMODE_TSC); return 0; } @@ -1124,7 +1124,7 @@ static struct clocksource clocksource_tsc_early = { .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_MUST_VERIFY, - .archdata = { .vclock_mode = VCLOCK_TSC }, + .vdso_clock_mode = VDSO_CLOCKMODE_TSC, .enable = tsc_cs_enable, .resume = tsc_resume, .mark_unstable = tsc_cs_mark_unstable, @@ -1145,7 +1145,7 @@ static struct clocksource clocksource_tsc = { .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_MUST_VERIFY, - .archdata = { .vclock_mode = VCLOCK_TSC }, + .vdso_clock_mode = VDSO_CLOCKMODE_TSC, .enable = tsc_cs_enable, .resume = tsc_resume, .mark_unstable = tsc_cs_mark_unstable, diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index f194dd058470..cef5a344fedb 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -815,8 +815,8 @@ TRACE_EVENT(kvm_write_tsc_offset, #ifdef CONFIG_X86_64 #define host_clocks \ - {VCLOCK_NONE, "none"}, \ - {VCLOCK_TSC, "tsc"} \ + {VDSO_CLOCKMODE_NONE, "none"}, \ + {VDSO_CLOCKMODE_TSC, "tsc"} \ TRACE_EVENT(kvm_update_master_clock, TP_PROTO(bool use_master_clock, unsigned int host_clock, bool offset_matched), diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index fb5d64ebc35d..0e7ef2955db3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1631,7 +1631,7 @@ static void update_pvclock_gtod(struct timekeeper *tk) write_seqcount_begin(&vdata->seq); /* copy pvclock gtod data */ - vdata->clock.vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode; + vdata->clock.vclock_mode = tk->tkr_mono.clock->vdso_clock_mode; vdata->clock.cycle_last = tk->tkr_mono.cycle_last; vdata->clock.mask = tk->tkr_mono.mask; vdata->clock.mult = tk->tkr_mono.mult; @@ -1639,7 +1639,7 @@ static void update_pvclock_gtod(struct timekeeper *tk) vdata->clock.base_cycles = tk->tkr_mono.xtime_nsec; vdata->clock.offset = tk->tkr_mono.base; - vdata->raw_clock.vclock_mode = tk->tkr_raw.clock->archdata.vclock_mode; + vdata->raw_clock.vclock_mode = tk->tkr_raw.clock->vdso_clock_mode; vdata->raw_clock.cycle_last = tk->tkr_raw.cycle_last; vdata->raw_clock.mask = tk->tkr_raw.mask; vdata->raw_clock.mult = tk->tkr_raw.mult; @@ -1840,7 +1840,7 @@ static u64 compute_guest_tsc(struct kvm_vcpu *vcpu, s64 kernel_ns) static inline int gtod_is_based_on_tsc(int mode) { - return mode == VCLOCK_TSC || mode == VCLOCK_HVCLOCK; + return mode == VDSO_CLOCKMODE_TSC || mode == VDSO_CLOCKMODE_HVCLOCK; } static void kvm_track_tsc_matching(struct kvm_vcpu *vcpu) @@ -1933,7 +1933,7 @@ static inline bool kvm_check_tsc_unstable(void) * TSC is marked unstable when we're running on Hyper-V, * 'TSC page' clocksource is good. */ - if (pvclock_gtod_data.clock.vclock_mode == VCLOCK_HVCLOCK) + if (pvclock_gtod_data.clock.vclock_mode == VDSO_CLOCKMODE_HVCLOCK) return false; #endif return check_tsc_unstable(); @@ -2088,30 +2088,30 @@ static inline u64 vgettsc(struct pvclock_clock *clock, u64 *tsc_timestamp, u64 tsc_pg_val; switch (clock->vclock_mode) { - case VCLOCK_HVCLOCK: + case VDSO_CLOCKMODE_HVCLOCK: tsc_pg_val = hv_read_tsc_page_tsc(hv_get_tsc_page(), tsc_timestamp); if (tsc_pg_val != U64_MAX) { /* TSC page valid */ - *mode = VCLOCK_HVCLOCK; + *mode = VDSO_CLOCKMODE_HVCLOCK; v = (tsc_pg_val - clock->cycle_last) & clock->mask; } else { /* TSC page invalid */ - *mode = VCLOCK_NONE; + *mode = VDSO_CLOCKMODE_NONE; } break; - case VCLOCK_TSC: - *mode = VCLOCK_TSC; + case VDSO_CLOCKMODE_TSC: + *mode = VDSO_CLOCKMODE_TSC; *tsc_timestamp = read_tsc(); v = (*tsc_timestamp - clock->cycle_last) & clock->mask; break; default: - *mode = VCLOCK_NONE; + *mode = VDSO_CLOCKMODE_NONE; } - if (*mode == VCLOCK_NONE) + if (*mode == VDSO_CLOCKMODE_NONE) *tsc_timestamp = v = 0; return v * clock->mult; diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 5d1568ff19ea..c8897aad13cd 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -147,7 +147,7 @@ static struct notifier_block xen_pvclock_gtod_notifier = { static int xen_cs_enable(struct clocksource *cs) { - vclocks_set_used(VCLOCK_PVCLOCK); + vclocks_set_used(VDSO_CLOCKMODE_PVCLOCK); return 0; } @@ -419,12 +419,13 @@ void xen_restore_time_memory_area(void) ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, 0, &t); /* - * We don't disable VCLOCK_PVCLOCK entirely if it fails to register the - * secondary time info with Xen or if we migrated to a host without the - * necessary flags. On both of these cases what happens is either - * process seeing a zeroed out pvti or seeing no PVCLOCK_TSC_STABLE_BIT - * bit set. Userspace checks the latter and if 0, it discards the data - * in pvti and fallbacks to a system call for a reliable timestamp. + * We don't disable VDSO_CLOCKMODE_PVCLOCK entirely if it fails to + * register the secondary time info with Xen or if we migrated to a + * host without the necessary flags. On both of these cases what + * happens is either process seeing a zeroed out pvti or seeing no + * PVCLOCK_TSC_STABLE_BIT bit set. Userspace checks the latter and + * if 0, it discards the data in pvti and fallbacks to a system + * call for a reliable timestamp. */ if (ret != 0) pr_notice("Cannot restore secondary vcpu_time_info (err %d)", @@ -450,7 +451,7 @@ static void xen_setup_vsyscall_time_info(void) ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, 0, &t); if (ret) { - pr_notice("xen: VCLOCK_PVCLOCK not supported (err %d)\n", ret); + pr_notice("xen: VDSO_CLOCKMODE_PVCLOCK not supported (err %d)\n", ret); free_page((unsigned long)ti); return; } @@ -467,14 +468,14 @@ static void xen_setup_vsyscall_time_info(void) if (!ret) free_page((unsigned long)ti); - pr_notice("xen: VCLOCK_PVCLOCK not supported (tsc unstable)\n"); + pr_notice("xen: VDSO_CLOCKMODE_PVCLOCK not supported (tsc unstable)\n"); return; } xen_clock = ti; pvclock_set_pvti_cpu0_va(xen_clock); - xen_clocksource.archdata.vclock_mode = VCLOCK_PVCLOCK; + xen_clocksource.vdso_clock_mode = VDSO_CLOCKMODE_PVCLOCK; } static void __init xen_time_init(void) From c2ae49285a7b21ae1f44ee0c44a5f5af0e2048dd Mon Sep 17 00:00:00 2001 From: Ajay Gupta Date: Mon, 17 Feb 2020 17:49:11 +0300 Subject: [PATCH 0289/2231] usb: typec: ucsi: register DP only for NVIDIA DP VDO NVIDIA VirtualLink (svid 0x955) has two altmode, vdo=0x1 for VirtualLink DP mode and vdo=0x3 for NVIDIA test mode. Register display altmode driver only for vdo=0x1 Signed-off-by: Ajay Gupta Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200217144913.55330-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 12 ++++++++++-- drivers/usb/typec/ucsi/ucsi.h | 7 +++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index b1b72cb7af10..ddf2ad3752de 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -270,9 +270,16 @@ static int ucsi_register_altmode(struct ucsi_connector *con, switch (desc->svid) { case USB_TYPEC_DP_SID: - case USB_TYPEC_NVIDIA_VLINK_SID: alt = ucsi_register_displayport(con, override, i, desc); break; + case USB_TYPEC_NVIDIA_VLINK_SID: + if (desc->vdo == USB_TYPEC_NVIDIA_VLINK_DBG_VDO) + alt = typec_port_register_altmode(con->port, + desc); + else + alt = ucsi_register_displayport(con, override, + i, desc); + break; default: alt = typec_port_register_altmode(con->port, desc); break; @@ -475,7 +482,8 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) while (adev[i]) { if (recipient == UCSI_RECIPIENT_SOP && (adev[i]->svid == USB_TYPEC_DP_SID || - adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID)) { + (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID && + adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) { pdev = typec_altmode_get_partner(adev[i]); ucsi_displayport_remove_partner((void *)pdev); } diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index e434b9c9a9eb..a89112b69cd5 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -340,4 +340,11 @@ static inline void ucsi_displayport_remove_partner(struct typec_altmode *adev) { } #endif /* CONFIG_TYPEC_DP_ALTMODE */ +/* + * NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink + * DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3 + */ +#define USB_TYPEC_NVIDIA_VLINK_DP_VDO 0x1 +#define USB_TYPEC_NVIDIA_VLINK_DBG_VDO 0x3 + #endif /* __DRIVER_USB_TYPEC_UCSI_H */ From 57a5e5f936be583d2c6cef3661c169e3ea4bf922 Mon Sep 17 00:00:00 2001 From: Ajay Gupta Date: Mon, 17 Feb 2020 17:49:12 +0300 Subject: [PATCH 0290/2231] usb: ucsi: ccg: disable runtime pm during fw flashing Ucsi ppm is unregistered during fw flashing so disable runtime pm also and reenable after fw flashing is completed and ppm is re-registered. Signed-off-by: Ajay Gupta Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200217144913.55330-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index a5b8530490db..2658cda5da11 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1219,6 +1219,7 @@ static int ccg_restart(struct ucsi_ccg *uc) return status; } + pm_runtime_enable(uc->dev); return 0; } @@ -1234,6 +1235,7 @@ static void ccg_update_firmware(struct work_struct *work) if (flash_mode != FLASH_NOT_NEEDED) { ucsi_unregister(uc->ucsi); + pm_runtime_disable(uc->dev); free_irq(uc->irq, uc); ccg_fw_update(uc, flash_mode); From fa4f4ce50cf2d3897bb677afd751dad7fcb2c81e Mon Sep 17 00:00:00 2001 From: Tetsuhiro Kohada Date: Mon, 17 Feb 2020 16:29:40 +0900 Subject: [PATCH 0291/2231] staging: exfat: remove DOSNAMEs. remove 'dos_name','short_name' and related definitions. 'dos_name' and 'short_name' are definitions before VFAT. These are never used in exFAT. Signed-off-by: Tetsuhiro Kohada Link: https://lore.kernel.org/r/20200217072941.34116-1-Kohada.Tetsuhiro@dc.MitsubishiElectric.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 52 +++-------------------------- drivers/staging/exfat/exfat_core.c | 47 +++++++++----------------- drivers/staging/exfat/exfat_super.c | 37 ++++++++------------ 3 files changed, 33 insertions(+), 103 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 05e21839b349..ca9d3b5a3076 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -189,8 +189,6 @@ static inline u16 get_row_index(u16 i) #define MAX_PATH_DEPTH 15 /* max depth of path name */ #define MAX_NAME_LENGTH 256 /* max len of filename including NULL */ #define MAX_PATH_LENGTH 260 /* max len of pathname including NULL */ -#define DOS_NAME_LENGTH 11 /* DOS filename length excluding NULL */ -#define DOS_PATH_LENGTH 80 /* DOS pathname length excluding NULL */ /* file attributes */ #define ATTR_NORMAL 0x0000 @@ -210,9 +208,6 @@ static inline u16 get_row_index(u16 i) #define NUM_UPCASE 2918 -#define DOS_CUR_DIR_NAME ". " -#define DOS_PAR_DIR_NAME ".. " - #ifdef __LITTLE_ENDIAN #define UNI_CUR_DIR_NAME ".\0" #define UNI_PAR_DIR_NAME ".\0.\0" @@ -261,10 +256,6 @@ struct file_id_t { struct dir_entry_t { char name[MAX_NAME_LENGTH * MAX_CHARSET_SIZE]; - - /* used only for FAT12/16/32, not used for exFAT */ - char short_name[DOS_NAME_LENGTH + 2]; - u32 attr; u64 Size; u32 num_subdirs; @@ -381,33 +372,6 @@ struct dentry_t { u8 dummy[32]; }; -struct dos_dentry_t { - u8 name[DOS_NAME_LENGTH]; - u8 attr; - u8 lcase; - u8 create_time_ms; - u8 create_time[2]; - u8 create_date[2]; - u8 access_date[2]; - u8 start_clu_hi[2]; - u8 modify_time[2]; - u8 modify_date[2]; - u8 start_clu_lo[2]; - u8 size[4]; -}; - -/* MS-DOS FAT extended directory entry (32 bytes) */ -struct ext_dentry_t { - u8 order; - u8 unicode_0_4[10]; - u8 attr; - u8 sysid; - u8 checksum; - u8 unicode_5_10[12]; - u8 start_clu[2]; - u8 unicode_11_12[4]; -}; - /* MS-DOS EXFAT file directory entry (32 bytes) */ struct file_dentry_t { u8 type; @@ -482,12 +446,6 @@ struct uentry_t { struct chain_t clu; }; -/* DOS name structure */ -struct dos_name_t { - u8 name[DOS_NAME_LENGTH]; - u8 name_case; -}; - /* unicode name structure */ struct uni_name_t { u16 name[MAX_NAME_LENGTH]; @@ -725,8 +683,7 @@ struct entry_set_cache_t *get_entry_set_in_dir(struct super_block *sb, u32 type, struct dentry_t **file_ep); void release_entry_set(struct entry_set_cache_t *es); -s32 count_dos_name_entries(struct super_block *sb, struct chain_t *p_dir, - u32 type); +s32 count_entries(struct super_block *sb, struct chain_t *p_dir, u32 type); void update_dir_checksum(struct super_block *sb, struct chain_t *p_dir, s32 entry); void update_dir_checksum_with_entry_set(struct super_block *sb, @@ -734,9 +691,8 @@ void update_dir_checksum_with_entry_set(struct super_block *sb, bool is_dir_empty(struct super_block *sb, struct chain_t *p_dir); /* name conversion functions */ -s32 get_num_entries_and_dos_name(struct super_block *sb, struct chain_t *p_dir, - struct uni_name_t *p_uniname, s32 *entries, - struct dos_name_t *p_dosname); +s32 get_num_entries(struct super_block *sb, struct chain_t *p_dir, + struct uni_name_t *p_uniname, s32 *entries); u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type); /* name resolution functions */ @@ -784,7 +740,7 @@ s32 exfat_count_used_clusters(struct super_block *sb); /* dir operation functions */ s32 exfat_find_dir_entry(struct super_block *sb, struct chain_t *p_dir, struct uni_name_t *p_uniname, s32 num_entries, - struct dos_name_t *p_dosname, u32 type); + u32 type); void exfat_delete_dir_entry(struct super_block *sb, struct chain_t *p_dir, s32 entry, s32 order, s32 num_entries); void exfat_get_uni_name_from_ext_entry(struct super_block *sb, diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c index 5a686289a1db..94a10c5984ac 100644 --- a/drivers/staging/exfat/exfat_core.c +++ b/drivers/staging/exfat/exfat_core.c @@ -1044,8 +1044,7 @@ static s32 exfat_init_dir_entry(struct super_block *sb, struct chain_t *p_dir, static s32 exfat_init_ext_entry(struct super_block *sb, struct chain_t *p_dir, s32 entry, s32 num_entries, - struct uni_name_t *p_uniname, - struct dos_name_t *p_dosname) + struct uni_name_t *p_uniname) { int i; sector_t sector; @@ -1687,7 +1686,7 @@ static s32 extract_uni_name_from_name_entry(struct name_dentry_t *ep, u16 *unina */ s32 exfat_find_dir_entry(struct super_block *sb, struct chain_t *p_dir, struct uni_name_t *p_uniname, s32 num_entries, - struct dos_name_t *p_dosname, u32 type) + u32 type) { int i = 0, dentry = 0, num_ext_entries = 0, len, step; s32 order = 0; @@ -1851,8 +1850,7 @@ s32 exfat_count_ext_entries(struct super_block *sb, struct chain_t *p_dir, return count; } -s32 count_dos_name_entries(struct super_block *sb, struct chain_t *p_dir, - u32 type) +s32 count_entries(struct super_block *sb, struct chain_t *p_dir, u32 type) { int i, count = 0; s32 dentries_per_clu; @@ -1964,11 +1962,10 @@ bool is_dir_empty(struct super_block *sb, struct chain_t *p_dir) */ /* input : dir, uni_name - * output : num_of_entry, dos_name(format : aaaaaa~1.bbb) + * output : num_of_entry */ -s32 get_num_entries_and_dos_name(struct super_block *sb, struct chain_t *p_dir, - struct uni_name_t *p_uniname, s32 *entries, - struct dos_name_t *p_dosname) +s32 get_num_entries(struct super_block *sb, struct chain_t *p_dir, + struct uni_name_t *p_uniname, s32 *entries) { s32 num_entries; @@ -2136,12 +2133,10 @@ s32 create_dir(struct inode *inode, struct chain_t *p_dir, s32 ret, dentry, num_entries; u64 size; struct chain_t clu; - struct dos_name_t dos_name; struct super_block *sb = inode->i_sb; struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info); - ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, - &dos_name); + ret = get_num_entries(sb, p_dir, p_uniname, &num_entries); if (ret) return ret; @@ -2174,8 +2169,7 @@ s32 create_dir(struct inode *inode, struct chain_t *p_dir, if (ret != 0) return ret; - ret = exfat_init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, - &dos_name); + ret = exfat_init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname); if (ret != 0) return ret; @@ -2200,11 +2194,9 @@ s32 create_file(struct inode *inode, struct chain_t *p_dir, struct uni_name_t *p_uniname, u8 mode, struct file_id_t *fid) { s32 ret, dentry, num_entries; - struct dos_name_t dos_name; struct super_block *sb = inode->i_sb; - ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, - &dos_name); + ret = get_num_entries(sb, p_dir, p_uniname, &num_entries); if (ret) return ret; @@ -2214,7 +2206,7 @@ s32 create_file(struct inode *inode, struct chain_t *p_dir, return -ENOSPC; /* (1) update the directory entry */ - /* fill the dos name directory entry information of the created file. + /* fill the directory entry information of the created file. * the first cluster is not determined yet. (0) */ ret = exfat_init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, @@ -2222,8 +2214,7 @@ s32 create_file(struct inode *inode, struct chain_t *p_dir, if (ret != 0) return ret; - ret = exfat_init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, - &dos_name); + ret = exfat_init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname); if (ret != 0) return ret; @@ -2276,7 +2267,6 @@ s32 exfat_rename_file(struct inode *inode, struct chain_t *p_dir, s32 oldentry, { s32 ret, newentry = -1, num_old_entries, num_new_entries; sector_t sector_old, sector_new; - struct dos_name_t dos_name; struct dentry_t *epold, *epnew; struct super_block *sb = inode->i_sb; @@ -2295,8 +2285,7 @@ s32 exfat_rename_file(struct inode *inode, struct chain_t *p_dir, s32 oldentry, } num_old_entries++; - ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, - &num_new_entries, &dos_name); + ret = get_num_entries(sb, p_dir, p_uniname, &num_new_entries); if (ret) { exfat_buf_unlock(sb, sector_old); return ret; @@ -2341,8 +2330,7 @@ s32 exfat_rename_file(struct inode *inode, struct chain_t *p_dir, s32 oldentry, exfat_buf_unlock(sb, sector_old); ret = exfat_init_ext_entry(sb, p_dir, newentry, - num_new_entries, p_uniname, - &dos_name); + num_new_entries, p_uniname); if (ret != 0) return ret; @@ -2360,8 +2348,7 @@ s32 exfat_rename_file(struct inode *inode, struct chain_t *p_dir, s32 oldentry, exfat_buf_unlock(sb, sector_old); ret = exfat_init_ext_entry(sb, p_dir, oldentry, - num_new_entries, p_uniname, - &dos_name); + num_new_entries, p_uniname); if (ret != 0) return ret; @@ -2378,7 +2365,6 @@ s32 move_file(struct inode *inode, struct chain_t *p_olddir, s32 oldentry, { s32 ret, newentry, num_new_entries, num_old_entries; sector_t sector_mov, sector_new; - struct dos_name_t dos_name; struct dentry_t *epmov, *epnew; struct super_block *sb = inode->i_sb; @@ -2402,8 +2388,7 @@ s32 move_file(struct inode *inode, struct chain_t *p_olddir, s32 oldentry, } num_old_entries++; - ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, - &num_new_entries, &dos_name); + ret = get_num_entries(sb, p_newdir, p_uniname, &num_new_entries); if (ret) { exfat_buf_unlock(sb, sector_mov); return ret; @@ -2445,7 +2430,7 @@ s32 move_file(struct inode *inode, struct chain_t *p_olddir, s32 oldentry, exfat_buf_unlock(sb, sector_mov); ret = exfat_init_ext_entry(sb, p_newdir, newentry, num_new_entries, - p_uniname, &dos_name); + p_uniname); if (ret != 0) return ret; diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index 8446444cf68a..f31f771a3dc0 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -539,7 +539,6 @@ static int ffsLookupFile(struct inode *inode, char *path, struct file_id_t *fid) int ret, dentry, num_entries; struct chain_t dir; struct uni_name_t uni_name; - struct dos_name_t dos_name; struct dentry_t *ep, *ep2; struct entry_set_cache_t *es = NULL; struct super_block *sb = inode->i_sb; @@ -559,14 +558,13 @@ static int ffsLookupFile(struct inode *inode, char *path, struct file_id_t *fid) if (ret) goto out; - ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, - &dos_name); + ret = get_num_entries(sb, &dir, &uni_name, &num_entries); if (ret) goto out; /* search the file name for directories */ dentry = exfat_find_dir_entry(sb, &dir, &uni_name, num_entries, - &dos_name, TYPE_ALL); + TYPE_ALL); if (dentry < -1) { ret = -ENOENT; goto out; @@ -1456,7 +1454,6 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) sizeof(struct date_time_t)); memset((char *)&info->access_timestamp, 0, sizeof(struct date_time_t)); - strcpy(info->short_name, "."); strcpy(info->name, "."); dir.dir = p_fs->root_dir; @@ -1471,7 +1468,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) p_fs->cluster_size_bits; } - count = count_dos_name_entries(sb, &dir, TYPE_DIR); + count = count_entries(sb, &dir, TYPE_DIR); if (count < 0) { ret = count; /* propagate error upward */ goto out; @@ -1538,7 +1535,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->Size = (u64)count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; - count = count_dos_name_entries(sb, &dir, TYPE_DIR); + count = count_entries(sb, &dir, TYPE_DIR); if (count < 0) { ret = count; /* propagate error upward */ goto out; @@ -2061,8 +2058,9 @@ static int exfat_readdir(struct file *filp, struct dir_context *ctx) struct super_block *sb = inode->i_sb; struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info); struct dir_entry_t de; + struct inode *tmp; unsigned long inum; - loff_t cpos; + loff_t cpos, i_pos; int err = 0; __lock_super(sb); @@ -2111,21 +2109,14 @@ get_new: if (!de.name[0]) goto end_of_dir; - if (!memcmp(de.short_name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { - inum = inode->i_ino; - } else if (!memcmp(de.short_name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { - inum = parent_ino(filp->f_path.dentry); + i_pos = ((loff_t)EXFAT_I(inode)->fid.start_clu << 32) | + ((EXFAT_I(inode)->fid.rwoffset - 1) & 0xffffffff); + tmp = exfat_iget(sb, i_pos); + if (tmp) { + inum = tmp->i_ino; + iput(tmp); } else { - loff_t i_pos = ((loff_t)EXFAT_I(inode)->fid.start_clu << 32) | - ((EXFAT_I(inode)->fid.rwoffset - 1) & 0xffffffff); - struct inode *tmp = exfat_iget(sb, i_pos); - - if (tmp) { - inum = tmp->i_ino; - iput(tmp); - } else { - inum = iunique(sb, EXFAT_ROOT_INO); - } + inum = iunique(sb, EXFAT_ROOT_INO); } if (!dir_emit(ctx, de.name, strlen(de.name), inum, @@ -3829,8 +3820,6 @@ static int __init init_exfat(void) int err; BUILD_BUG_ON(sizeof(struct dentry_t) != DENTRY_SIZE); - BUILD_BUG_ON(sizeof(struct dos_dentry_t) != DENTRY_SIZE); - BUILD_BUG_ON(sizeof(struct ext_dentry_t) != DENTRY_SIZE); BUILD_BUG_ON(sizeof(struct file_dentry_t) != DENTRY_SIZE); BUILD_BUG_ON(sizeof(struct strm_dentry_t) != DENTRY_SIZE); BUILD_BUG_ON(sizeof(struct name_dentry_t) != DENTRY_SIZE); From 23aa10344c627c8ba969049dc456bcb4021d7ed2 Mon Sep 17 00:00:00 2001 From: Tetsuhiro Kohada Date: Mon, 17 Feb 2020 16:29:41 +0900 Subject: [PATCH 0292/2231] staging: exfat: dedicate count_entries() to sub-dir counting. count_entries() function is only used to count sub-dirs. Clarify the role and rename to count_dir_entries(). Signed-off-by: Tetsuhiro Kohada Link: https://lore.kernel.org/r/20200217072941.34116-2-Kohada.Tetsuhiro@dc.MitsubishiElectric.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 2 +- drivers/staging/exfat/exfat_core.c | 8 ++------ drivers/staging/exfat/exfat_super.c | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index ca9d3b5a3076..c4ef6c2de329 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -683,7 +683,7 @@ struct entry_set_cache_t *get_entry_set_in_dir(struct super_block *sb, u32 type, struct dentry_t **file_ep); void release_entry_set(struct entry_set_cache_t *es); -s32 count_entries(struct super_block *sb, struct chain_t *p_dir, u32 type); +s32 count_dir_entries(struct super_block *sb, struct chain_t *p_dir); void update_dir_checksum(struct super_block *sb, struct chain_t *p_dir, s32 entry); void update_dir_checksum_with_entry_set(struct super_block *sb, diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c index 94a10c5984ac..7308e50c0aaf 100644 --- a/drivers/staging/exfat/exfat_core.c +++ b/drivers/staging/exfat/exfat_core.c @@ -1850,7 +1850,7 @@ s32 exfat_count_ext_entries(struct super_block *sb, struct chain_t *p_dir, return count; } -s32 count_entries(struct super_block *sb, struct chain_t *p_dir, u32 type) +s32 count_dir_entries(struct super_block *sb, struct chain_t *p_dir) { int i, count = 0; s32 dentries_per_clu; @@ -1881,11 +1881,7 @@ s32 count_entries(struct super_block *sb, struct chain_t *p_dir, u32 type) if (entry_type == TYPE_UNUSED) return count; - if (!(type & TYPE_CRITICAL_PRI) && - !(type & TYPE_BENIGN_PRI)) - continue; - - if ((type == TYPE_ALL) || (type == entry_type)) + if (entry_type == TYPE_DIR) count++; } diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index f31f771a3dc0..b398114c2604 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -1468,7 +1468,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) p_fs->cluster_size_bits; } - count = count_entries(sb, &dir, TYPE_DIR); + count = count_dir_entries(sb, &dir); if (count < 0) { ret = count; /* propagate error upward */ goto out; @@ -1535,7 +1535,7 @@ static int ffsReadStat(struct inode *inode, struct dir_entry_t *info) info->Size = (u64)count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; - count = count_entries(sb, &dir, TYPE_DIR); + count = count_dir_entries(sb, &dir); if (count < 0) { ret = count; /* propagate error upward */ goto out; From 55b9b8f67d936b9e4ba26187a2619a005d9fc477 Mon Sep 17 00:00:00 2001 From: Sandesh Kenjana Ashok Date: Sat, 15 Feb 2020 00:21:43 +0100 Subject: [PATCH 0293/2231] staging: fsl-dpaa2: ethsw: ethsw.c: Fix line over 80 characters Issue found by checkpatch. Signed-off-by: Sandesh Kenjana Ashok Link: https://lore.kernel.org/r/20200214232143.GA20675@SandeshPC Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c index 39c0fe347188..676d1ad1b50d 100644 --- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c +++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c @@ -1492,7 +1492,8 @@ static void ethsw_unregister_notifier(struct device *dev) err = unregister_switchdev_blocking_notifier(nb); if (err) dev_err(dev, - "Failed to unregister switchdev blocking notifier (%d)\n", err); + "Failed to unregister switchdev blocking notifier (%d)\n", + err); err = unregister_switchdev_notifier(ðsw->port_switchdev_nb); if (err) From e1bdb22ebe5363ed75ddedf836ca9f19e1195337 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:57 +0100 Subject: [PATCH 0294/2231] mips: vdso: Use generic VDSO clock mode storage Switch to the generic VDSO clock mode storage. Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.244684017@linutronix.de --- arch/mips/Kconfig | 2 +- arch/mips/include/asm/clocksource.h | 18 +++----------- arch/mips/include/asm/vdso/gettimeofday.h | 30 ++++++++--------------- arch/mips/include/asm/vdso/vsyscall.h | 9 ------- arch/mips/kernel/csrc-r4k.c | 2 +- drivers/clocksource/mips-gic-timer.c | 8 +++--- 6 files changed, 19 insertions(+), 50 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 797d7f1ad5fe..23b5c0578776 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -4,7 +4,6 @@ config MIPS default y select ARCH_32BIT_OFF_T if !64BIT select ARCH_BINFMT_ELF_STATE if MIPS_FP_SUPPORT - select ARCH_CLOCKSOURCE_DATA select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_KCOV select ARCH_HAS_PTE_SPECIAL if !(32BIT && CPU_HAS_RIXI) @@ -38,6 +37,7 @@ config MIPS select GENERIC_SCHED_CLOCK if !CAVIUM_OCTEON_SOC select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL + select GENERIC_VDSO_CLOCK_MODE select GUP_GET_PTE_LOW_HIGH if CPU_MIPS32 && PHYS_ADDR_T_64BIT select HANDLE_DOMAIN_IRQ select HAVE_ARCH_COMPILER_H diff --git a/arch/mips/include/asm/clocksource.h b/arch/mips/include/asm/clocksource.h index cab9ae9f1e14..de659cae0d4e 100644 --- a/arch/mips/include/asm/clocksource.h +++ b/arch/mips/include/asm/clocksource.h @@ -3,23 +3,11 @@ * Copyright (C) 2015 Imagination Technologies * Author: Alex Smith */ - #ifndef __ASM_CLOCKSOURCE_H #define __ASM_CLOCKSOURCE_H -#include - -/* VDSO clocksources. */ -#define VDSO_CLOCK_NONE 0 /* No suitable clocksource. */ -#define VDSO_CLOCK_R4K 1 /* Use the coprocessor 0 count. */ -#define VDSO_CLOCK_GIC 2 /* Use the GIC. */ - -/** - * struct arch_clocksource_data - Architecture-specific clocksource information. - * @vdso_clock_mode: Method the VDSO should use to access the clocksource. - */ -struct arch_clocksource_data { - u8 vdso_clock_mode; -}; +#define VDSO_ARCH_CLOCKMODES \ + VDSO_CLOCKMODE_R4K, \ + VDSO_CLOCKMODE_GIC #endif /* __ASM_CLOCKSOURCE_H */ diff --git a/arch/mips/include/asm/vdso/gettimeofday.h b/arch/mips/include/asm/vdso/gettimeofday.h index a9f846b1a920..88c3de1bdf22 100644 --- a/arch/mips/include/asm/vdso/gettimeofday.h +++ b/arch/mips/include/asm/vdso/gettimeofday.h @@ -24,8 +24,6 @@ #define VDSO_HAS_CLOCK_GETRES 1 -#define __VDSO_USE_SYSCALL ULLONG_MAX - static __always_inline long gettimeofday_fallback( struct __kernel_old_timeval *_tv, struct timezone *_tz) @@ -175,28 +173,20 @@ static __always_inline u64 read_gic_count(const struct vdso_data *data) static __always_inline u64 __arch_get_hw_counter(s32 clock_mode) { -#ifdef CONFIG_CLKSRC_MIPS_GIC - const struct vdso_data *data = get_vdso_data(); -#endif - u64 cycle_now; - - switch (clock_mode) { #ifdef CONFIG_CSRC_R4K - case VDSO_CLOCK_R4K: - cycle_now = read_r4k_count(); - break; + if (clock_mode == VDSO_CLOCKMODE_R4K) + return read_r4k_count(); #endif #ifdef CONFIG_CLKSRC_MIPS_GIC - case VDSO_CLOCK_GIC: - cycle_now = read_gic_count(data); - break; + if (clock_mode == VDSO_CLOCKMODE_GIC) + return read_gic_count(get_vdso_data()); #endif - default: - cycle_now = __VDSO_USE_SYSCALL; - break; - } - - return cycle_now; + /* + * Core checks mode already. So this raced against a concurrent + * update. Return something. Core will do another round see the + * change and fallback to syscall. + */ + return 0; } static inline bool mips_vdso_hres_capable(void) diff --git a/arch/mips/include/asm/vdso/vsyscall.h b/arch/mips/include/asm/vdso/vsyscall.h index 00d41b94ba31..47168aaf1eff 100644 --- a/arch/mips/include/asm/vdso/vsyscall.h +++ b/arch/mips/include/asm/vdso/vsyscall.h @@ -19,15 +19,6 @@ struct vdso_data *__mips_get_k_vdso_data(void) } #define __arch_get_k_vdso_data __mips_get_k_vdso_data -static __always_inline -int __mips_get_clock_mode(struct timekeeper *tk) -{ - u32 clock_mode = tk->tkr_mono.clock->archdata.vdso_clock_mode; - - return clock_mode; -} -#define __arch_get_clock_mode __mips_get_clock_mode - /* The asm-generic header needs to be included after the definitions above */ #include diff --git a/arch/mips/kernel/csrc-r4k.c b/arch/mips/kernel/csrc-r4k.c index eed099f35bf1..437dda64fd7a 100644 --- a/arch/mips/kernel/csrc-r4k.c +++ b/arch/mips/kernel/csrc-r4k.c @@ -78,7 +78,7 @@ int __init init_r4k_clocksource(void) * by the VDSO (HWREna is configured by configure_hwrena()). */ if (cpu_has_mips_r2_r6 && rdhwr_count_usable()) - clocksource_mips.archdata.vdso_clock_mode = VDSO_CLOCK_R4K; + clocksource_mips.vdso_clock_mode = VDSO_CLOCKMODE_R4K; clocksource_register_hz(&clocksource_mips, mips_hpt_frequency); diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c index 37671a5d4ed9..8b5f8ae723cb 100644 --- a/drivers/clocksource/mips-gic-timer.c +++ b/drivers/clocksource/mips-gic-timer.c @@ -155,10 +155,10 @@ static u64 gic_hpt_read(struct clocksource *cs) } static struct clocksource gic_clocksource = { - .name = "GIC", - .read = gic_hpt_read, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .archdata = { .vdso_clock_mode = VDSO_CLOCK_GIC }, + .name = "GIC", + .read = gic_hpt_read, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .vdso_clock_mode = VDSO_CLOCKMODE_GIC, }; static int __init __gic_clocksource_init(void) From 5e3c6a312a0946d2d83e32359612cbb925a8bed0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:58 +0100 Subject: [PATCH 0295/2231] ARM/arm64: vdso: Use common vdso clock mode storage Convert ARM/ARM64 to the generic VDSO clock mode storage. This needs to happen in one go as they share the clocksource driver. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.363235229@linutronix.de --- arch/arm/Kconfig | 1 - arch/arm/include/asm/clocksource.h | 5 ++--- arch/arm/include/asm/vdso/gettimeofday.h | 12 ++++++++--- arch/arm/include/asm/vdso/vsyscall.h | 21 ------------------- arch/arm/mm/Kconfig | 1 + arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/clocksource.h | 5 ++--- .../include/asm/vdso/compat_gettimeofday.h | 11 +++++----- arch/arm64/include/asm/vdso/gettimeofday.h | 11 +++++----- arch/arm64/include/asm/vdso/vsyscall.h | 9 -------- drivers/clocksource/arm_arch_timer.c | 8 +++---- 11 files changed, 29 insertions(+), 57 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 97864aabc2a6..03bbfc312fe7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -3,7 +3,6 @@ config ARM bool default y select ARCH_32BIT_OFF_T - select ARCH_CLOCKSOURCE_DATA select ARCH_HAS_BINFMT_FLAT select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEVMEM_IS_ALLOWED diff --git a/arch/arm/include/asm/clocksource.h b/arch/arm/include/asm/clocksource.h index 0b350a7e26f3..73beb7f131de 100644 --- a/arch/arm/include/asm/clocksource.h +++ b/arch/arm/include/asm/clocksource.h @@ -1,8 +1,7 @@ #ifndef _ASM_CLOCKSOURCE_H #define _ASM_CLOCKSOURCE_H -struct arch_clocksource_data { - bool vdso_direct; /* Usable for direct VDSO access? */ -}; +#define VDSO_ARCH_CLOCKMODES \ + VDSO_CLOCKMODE_ARCHTIMER #endif diff --git a/arch/arm/include/asm/vdso/gettimeofday.h b/arch/arm/include/asm/vdso/gettimeofday.h index f4757d327f43..07d791c65cf7 100644 --- a/arch/arm/include/asm/vdso/gettimeofday.h +++ b/arch/arm/include/asm/vdso/gettimeofday.h @@ -117,15 +117,21 @@ static __always_inline u64 __arch_get_hw_counter(int clock_mode) #ifdef CONFIG_ARM_ARCH_TIMER u64 cycle_now; - if (!clock_mode) - return -EINVAL; + /* + * Core checks for mode already, so this raced against a concurrent + * update. Return something. Core will do another round and then + * see the mode change and fallback to the syscall. + */ + if (clock_mode == VDSO_CLOCKMODE_NONE) + return 0; isb(); cycle_now = read_sysreg(CNTVCT); return cycle_now; #else - return -EINVAL; /* use fallback */ + /* Make GCC happy. This is compiled out anyway */ + return 0; #endif } diff --git a/arch/arm/include/asm/vdso/vsyscall.h b/arch/arm/include/asm/vdso/vsyscall.h index 85a7e58b7228..002f9edd8a2b 100644 --- a/arch/arm/include/asm/vdso/vsyscall.h +++ b/arch/arm/include/asm/vdso/vsyscall.h @@ -11,18 +11,6 @@ extern struct vdso_data *vdso_data; extern bool cntvct_ok; -static __always_inline -bool tk_is_cntvct(const struct timekeeper *tk) -{ - if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER)) - return false; - - if (!tk->tkr_mono.clock->archdata.vdso_direct) - return false; - - return true; -} - /* * Update the vDSO data page to keep in sync with kernel timekeeping. */ @@ -40,15 +28,6 @@ bool __arm_update_vdso_data(void) } #define __arch_update_vdso_data __arm_update_vdso_data -static __always_inline -int __arm_get_clock_mode(struct timekeeper *tk) -{ - u32 __tk_is_cntvct = tk_is_cntvct(tk); - - return __tk_is_cntvct; -} -#define __arch_get_clock_mode __arm_get_clock_mode - static __always_inline void __arm_sync_vdso_data(struct vdso_data *vdata) { diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 65e4482e3849..865e888bb84f 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -900,6 +900,7 @@ config VDSO select GENERIC_TIME_VSYSCALL select GENERIC_VDSO_32 select GENERIC_GETTIMEOFDAY + select GENERIC_VDSO_CLOCK_MODE help Place in the process address space an ELF shared object providing fast implementations of gettimeofday and diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 0b30e884e088..7809d4976269 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -9,7 +9,6 @@ config ARM64 select ACPI_MCFG if (ACPI && PCI) select ACPI_SPCR_TABLE if ACPI select ACPI_PPTT if ACPI - select ARCH_CLOCKSOURCE_DATA select ARCH_HAS_DEBUG_VIRTUAL select ARCH_HAS_DEVMEM_IS_ALLOWED select ARCH_HAS_DMA_PREP_COHERENT @@ -111,6 +110,7 @@ config ARM64 select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select GENERIC_GETTIMEOFDAY + select GENERIC_VDSO_CLOCK_MODE select HANDLE_DOMAIN_IRQ select HARDIRQS_SW_RESEND select HAVE_PCI diff --git a/arch/arm64/include/asm/clocksource.h b/arch/arm64/include/asm/clocksource.h index 0ece64a26c8c..eb82e9d95c5d 100644 --- a/arch/arm64/include/asm/clocksource.h +++ b/arch/arm64/include/asm/clocksource.h @@ -2,8 +2,7 @@ #ifndef _ASM_CLOCKSOURCE_H #define _ASM_CLOCKSOURCE_H -struct arch_clocksource_data { - bool vdso_direct; /* Usable for direct VDSO access? */ -}; +#define VDSO_ARCH_CLOCKMODES \ + VDSO_CLOCKMODE_ARCHTIMER #endif diff --git a/arch/arm64/include/asm/vdso/compat_gettimeofday.h b/arch/arm64/include/asm/vdso/compat_gettimeofday.h index 537b1e695365..81b0c394f1d8 100644 --- a/arch/arm64/include/asm/vdso/compat_gettimeofday.h +++ b/arch/arm64/include/asm/vdso/compat_gettimeofday.h @@ -12,8 +12,6 @@ #include -#define __VDSO_USE_SYSCALL ULLONG_MAX - #define VDSO_HAS_CLOCK_GETRES 1 #define BUILD_VDSO32 1 @@ -117,11 +115,12 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode) u64 res; /* - * clock_mode == 0 implies that vDSO are enabled otherwise - * fallback on syscall. + * Core checks for mode already, so this raced against a concurrent + * update. Return something. Core will do another round and then + * see the mode change and fallback to the syscall. */ - if (clock_mode) - return __VDSO_USE_SYSCALL; + if (clock_mode == VDSO_CLOCKMODE_NONE) + return 0; /* * This isb() is required to prevent that the counter value diff --git a/arch/arm64/include/asm/vdso/gettimeofday.h b/arch/arm64/include/asm/vdso/gettimeofday.h index b08f476b72b4..5a534432aa5d 100644 --- a/arch/arm64/include/asm/vdso/gettimeofday.h +++ b/arch/arm64/include/asm/vdso/gettimeofday.h @@ -10,8 +10,6 @@ #include #include -#define __VDSO_USE_SYSCALL ULLONG_MAX - #define VDSO_HAS_CLOCK_GETRES 1 static __always_inline @@ -71,11 +69,12 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode) u64 res; /* - * clock_mode == 0 implies that vDSO are enabled otherwise - * fallback on syscall. + * Core checks for mode already, so this raced against a concurrent + * update. Return something. Core will do another round and then + * see the mode change and fallback to the syscall. */ - if (clock_mode) - return __VDSO_USE_SYSCALL; + if (clock_mode == VDSO_CLOCKMODE_NONE) + return 0; /* * This isb() is required to prevent that the counter value diff --git a/arch/arm64/include/asm/vdso/vsyscall.h b/arch/arm64/include/asm/vdso/vsyscall.h index 0c20a7c1bee5..f94b1457c117 100644 --- a/arch/arm64/include/asm/vdso/vsyscall.h +++ b/arch/arm64/include/asm/vdso/vsyscall.h @@ -21,15 +21,6 @@ struct vdso_data *__arm64_get_k_vdso_data(void) } #define __arch_get_k_vdso_data __arm64_get_k_vdso_data -static __always_inline -int __arm64_get_clock_mode(struct timekeeper *tk) -{ - u32 use_syscall = !tk->tkr_mono.clock->archdata.vdso_direct; - - return use_syscall; -} -#define __arch_get_clock_mode __arm64_get_clock_mode - static __always_inline void __arm64_update_vsyscall(struct vdso_data *vdata, struct timekeeper *tk) { diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 9a5464c625b4..ee2420d56f67 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -69,7 +69,7 @@ static enum arch_timer_ppi_nr arch_timer_uses_ppi = ARCH_TIMER_VIRT_PPI; static bool arch_timer_c3stop; static bool arch_timer_mem_use_virtual; static bool arch_counter_suspend_stop; -static bool vdso_default = true; +static enum vdso_clock_mode vdso_default = VDSO_CLOCKMODE_ARCHTIMER; static cpumask_t evtstrm_available = CPU_MASK_NONE; static bool evtstrm_enable = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM); @@ -560,8 +560,8 @@ void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa * change both the default value and the vdso itself. */ if (wa->read_cntvct_el0) { - clocksource_counter.archdata.vdso_direct = false; - vdso_default = false; + clocksource_counter.vdso_clock_mode = VDSO_CLOCKMODE_NONE; + vdso_default = VDSO_CLOCKMODE_NONE; } } @@ -979,7 +979,7 @@ static void __init arch_counter_register(unsigned type) } arch_timer_read_counter = rd; - clocksource_counter.archdata.vdso_direct = vdso_default; + clocksource_counter.vdso_clock_mode = vdso_default; } else { arch_timer_read_counter = arch_counter_get_cntvct_mem; } From f86fd32db706613fe8d0104057efa6e83e0d7e8f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:59 +0100 Subject: [PATCH 0296/2231] lib/vdso: Cleanup clock mode storage leftovers Now that all architectures are converted to use the generic storage the helpers and conditionals can be removed. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.470699892@linutronix.de --- arch/arm/mm/Kconfig | 1 - arch/arm64/Kconfig | 1 - arch/mips/Kconfig | 1 - arch/x86/Kconfig | 1 - include/asm-generic/vdso/vsyscall.h | 7 ------- include/linux/clocksource.h | 4 ++-- kernel/time/vsyscall.c | 4 ---- lib/vdso/Kconfig | 3 --- lib/vdso/gettimeofday.c | 17 +++++------------ 9 files changed, 7 insertions(+), 32 deletions(-) diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 865e888bb84f..65e4482e3849 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -900,7 +900,6 @@ config VDSO select GENERIC_TIME_VSYSCALL select GENERIC_VDSO_32 select GENERIC_GETTIMEOFDAY - select GENERIC_VDSO_CLOCK_MODE help Place in the process address space an ELF shared object providing fast implementations of gettimeofday and diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7809d4976269..c6c32fb7f546 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -110,7 +110,6 @@ config ARM64 select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select GENERIC_GETTIMEOFDAY - select GENERIC_VDSO_CLOCK_MODE select HANDLE_DOMAIN_IRQ select HARDIRQS_SW_RESEND select HAVE_PCI diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 23b5c0578776..654369a7209d 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -37,7 +37,6 @@ config MIPS select GENERIC_SCHED_CLOCK if !CAVIUM_OCTEON_SOC select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL - select GENERIC_VDSO_CLOCK_MODE select GUP_GET_PTE_LOW_HIGH if CPU_MIPS32 && PHYS_ADDR_T_64BIT select HANDLE_DOMAIN_IRQ select HAVE_ARCH_COMPILER_H diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 698e9c835cc5..8b995db3d10f 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -125,7 +125,6 @@ config X86 select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select GENERIC_GETTIMEOFDAY - select GENERIC_VDSO_CLOCK_MODE select GENERIC_VDSO_TIME_NS select GUP_GET_PTE_LOW_HIGH if X86_PAE select HARDLOCKUP_CHECK_TIMESTAMP if X86_64 diff --git a/include/asm-generic/vdso/vsyscall.h b/include/asm-generic/vdso/vsyscall.h index cec543d9e87b..4a28797495d7 100644 --- a/include/asm-generic/vdso/vsyscall.h +++ b/include/asm-generic/vdso/vsyscall.h @@ -18,13 +18,6 @@ static __always_inline bool __arch_update_vdso_data(void) } #endif /* __arch_update_vdso_data */ -#ifndef __arch_get_clock_mode -static __always_inline int __arch_get_clock_mode(struct timekeeper *tk) -{ - return 0; -} -#endif /* __arch_get_clock_mode */ - #ifndef __arch_update_vsyscall static __always_inline void __arch_update_vsyscall(struct vdso_data *vdata, struct timekeeper *tk) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 6d5ed1b4d24d..7fefe0b21a14 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -24,13 +24,13 @@ struct clocksource; struct module; #if defined(CONFIG_ARCH_CLOCKSOURCE_DATA) || \ - defined(CONFIG_GENERIC_VDSO_CLOCK_MODE) + defined(CONFIG_GENERIC_GETTIMEOFDAY) #include #endif enum vdso_clock_mode { VDSO_CLOCKMODE_NONE, -#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE +#ifdef CONFIG_GENERIC_GETTIMEOFDAY VDSO_ARCH_CLOCKMODES, #endif VDSO_CLOCKMODE_MAX, diff --git a/kernel/time/vsyscall.c b/kernel/time/vsyscall.c index f9a5178c69bb..d31a5ef4ade5 100644 --- a/kernel/time/vsyscall.c +++ b/kernel/time/vsyscall.c @@ -77,11 +77,7 @@ void update_vsyscall(struct timekeeper *tk) /* copy vsyscall data */ vdso_write_begin(vdata); -#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE clock_mode = tk->tkr_mono.clock->vdso_clock_mode; -#else - clock_mode = __arch_get_clock_mode(tk); -#endif vdata[CS_HRES_COARSE].clock_mode = clock_mode; vdata[CS_RAW].clock_mode = clock_mode; diff --git a/lib/vdso/Kconfig b/lib/vdso/Kconfig index d9f43c84fcc6..d883ac299508 100644 --- a/lib/vdso/Kconfig +++ b/lib/vdso/Kconfig @@ -30,7 +30,4 @@ config GENERIC_VDSO_TIME_NS Selected by architectures which support time namespaces in the VDSO -config GENERIC_VDSO_CLOCK_MODE - bool - endif diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index 3f2d8b859130..00f8d1f1405b 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -65,16 +65,13 @@ static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk, do { seq = vdso_read_begin(vd); - if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && - vd->clock_mode == VDSO_CLOCKMODE_NONE) + + if (unlikely(vd->clock_mode == VDSO_CLOCKMODE_NONE)) return -1; + cycles = __arch_get_hw_counter(vd->clock_mode); ns = vdso_ts->nsec; last = vd->cycle_last; - if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && - unlikely((s64)cycles < 0)) - return -1; - ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); ns >>= vd->shift; sec = vdso_ts->sec; @@ -137,16 +134,12 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, } smp_rmb(); - if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && - vd->clock_mode == VDSO_CLOCKMODE_NONE) + if (unlikely(vd->clock_mode == VDSO_CLOCKMODE_NONE)) return -1; + cycles = __arch_get_hw_counter(vd->clock_mode); ns = vdso_ts->nsec; last = vd->cycle_last; - if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && - unlikely((s64)cycles < 0)) - return -1; - ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); ns >>= vd->shift; sec = vdso_ts->sec; From c7a18100bdffdff440c7291db6e80863fab0461e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:39:00 +0100 Subject: [PATCH 0297/2231] lib/vdso: Avoid highres update if clocksource is not VDSO capable If the current clocksource is not VDSO capable there is no point in updating the high resolution parts of the VDSO data. Replace the architecture specific check with a check for a VDSO capable clocksource and skip the update if there is none. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.563379423@linutronix.de --- arch/arm/include/asm/vdso/vsyscall.h | 7 ------- include/asm-generic/vdso/vsyscall.h | 7 ------- kernel/time/vsyscall.c | 6 +++--- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/arch/arm/include/asm/vdso/vsyscall.h b/arch/arm/include/asm/vdso/vsyscall.h index 002f9edd8a2b..47e41ae8ccd0 100644 --- a/arch/arm/include/asm/vdso/vsyscall.h +++ b/arch/arm/include/asm/vdso/vsyscall.h @@ -21,13 +21,6 @@ struct vdso_data *__arm_get_k_vdso_data(void) } #define __arch_get_k_vdso_data __arm_get_k_vdso_data -static __always_inline -bool __arm_update_vdso_data(void) -{ - return cntvct_ok; -} -#define __arch_update_vdso_data __arm_update_vdso_data - static __always_inline void __arm_sync_vdso_data(struct vdso_data *vdata) { diff --git a/include/asm-generic/vdso/vsyscall.h b/include/asm-generic/vdso/vsyscall.h index 4a28797495d7..c835607f78ae 100644 --- a/include/asm-generic/vdso/vsyscall.h +++ b/include/asm-generic/vdso/vsyscall.h @@ -11,13 +11,6 @@ static __always_inline struct vdso_data *__arch_get_k_vdso_data(void) } #endif /* __arch_get_k_vdso_data */ -#ifndef __arch_update_vdso_data -static __always_inline bool __arch_update_vdso_data(void) -{ - return true; -} -#endif /* __arch_update_vdso_data */ - #ifndef __arch_update_vsyscall static __always_inline void __arch_update_vsyscall(struct vdso_data *vdata, struct timekeeper *tk) diff --git a/kernel/time/vsyscall.c b/kernel/time/vsyscall.c index d31a5ef4ade5..54ce6eb2ca36 100644 --- a/kernel/time/vsyscall.c +++ b/kernel/time/vsyscall.c @@ -105,10 +105,10 @@ void update_vsyscall(struct timekeeper *tk) WRITE_ONCE(vdata[CS_HRES_COARSE].hrtimer_res, hrtimer_resolution); /* - * Architectures can opt out of updating the high resolution part - * of the VDSO. + * If the current clocksource is not VDSO capable, then spare the + * update of the high reolution parts. */ - if (__arch_update_vdso_data()) + if (clock_mode != VDSO_CLOCKMODE_NONE) update_vdso_data(vdata, tk); __arch_update_vsyscall(vdata, tk); From 2d6b01bd88ccabba06d342ef80eaab6b39d12497 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:39:01 +0100 Subject: [PATCH 0298/2231] lib/vdso: Move VCLOCK_TIMENS to vdso_clock_modes Move the time namespace indicator clock mode to the other ones for consistency sake. Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.656097274@linutronix.de --- include/linux/clocksource.h | 3 +++ include/vdso/datapage.h | 2 -- kernel/time/namespace.c | 7 ++++--- lib/vdso/gettimeofday.c | 18 ++++++++++-------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 7fefe0b21a14..02e3282719bd 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -34,6 +34,9 @@ enum vdso_clock_mode { VDSO_ARCH_CLOCKMODES, #endif VDSO_CLOCKMODE_MAX, + + /* Indicator for time namespace VDSO */ + VDSO_CLOCKMODE_TIMENS = INT_MAX }; /** diff --git a/include/vdso/datapage.h b/include/vdso/datapage.h index c5f347cc5e55..30c4cb0428d1 100644 --- a/include/vdso/datapage.h +++ b/include/vdso/datapage.h @@ -21,8 +21,6 @@ #define CS_RAW 1 #define CS_BASES (CS_RAW + 1) -#define VCLOCK_TIMENS UINT_MAX - /** * struct vdso_timestamp - basetime per clock_id * @sec: seconds diff --git a/kernel/time/namespace.c b/kernel/time/namespace.c index 12858507d75a..e6ba064ce773 100644 --- a/kernel/time/namespace.c +++ b/kernel/time/namespace.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -172,8 +173,8 @@ static struct timens_offset offset_from_ts(struct timespec64 off) * for vdso_data->clock_mode is a non-issue. The task is spin waiting for the * update to finish and for 'seq' to become even anyway. * - * Timens page has vdso_data->clock_mode set to VCLOCK_TIMENS which enforces - * the time namespace handling path. + * Timens page has vdso_data->clock_mode set to VDSO_CLOCKMODE_TIMENS which + * enforces the time namespace handling path. */ static void timens_setup_vdso_data(struct vdso_data *vdata, struct time_namespace *ns) @@ -183,7 +184,7 @@ static void timens_setup_vdso_data(struct vdso_data *vdata, struct timens_offset boottime = offset_from_ts(ns->offsets.boottime); vdata->seq = 1; - vdata->clock_mode = VCLOCK_TIMENS; + vdata->clock_mode = VDSO_CLOCKMODE_TIMENS; offset[CLOCK_MONOTONIC] = monotonic; offset[CLOCK_MONOTONIC_RAW] = monotonic; offset[CLOCK_MONOTONIC_COARSE] = monotonic; diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index 00f8d1f1405b..a76ac8d17c5f 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -116,10 +116,10 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, do { /* - * Open coded to handle VCLOCK_TIMENS. Time namespace + * Open coded to handle VDSO_CLOCKMODE_TIMENS. Time namespace * enabled tasks have a special VVAR page installed which * has vd->seq set to 1 and vd->clock_mode set to - * VCLOCK_TIMENS. For non time namespace affected tasks + * VDSO_CLOCKMODE_TIMENS. For non time namespace affected tasks * this does not affect performance because if vd->seq is * odd, i.e. a concurrent update is in progress the extra * check for vd->clock_mode is just a few extra @@ -128,7 +128,7 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, */ while (unlikely((seq = READ_ONCE(vd->seq)) & 1)) { if (IS_ENABLED(CONFIG_TIME_NS) && - vd->clock_mode == VCLOCK_TIMENS) + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) return do_hres_timens(vd, clk, ts); cpu_relax(); } @@ -200,12 +200,12 @@ static __always_inline int do_coarse(const struct vdso_data *vd, clockid_t clk, do { /* - * Open coded to handle VCLOCK_TIMENS. See comment in + * Open coded to handle VDSO_CLOCK_TIMENS. See comment in * do_hres(). */ while ((seq = READ_ONCE(vd->seq)) & 1) { if (IS_ENABLED(CONFIG_TIME_NS) && - vd->clock_mode == VCLOCK_TIMENS) + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) return do_coarse_timens(vd, clk, ts); cpu_relax(); } @@ -292,7 +292,7 @@ __cvdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz) if (unlikely(tz != NULL)) { if (IS_ENABLED(CONFIG_TIME_NS) && - vd->clock_mode == VCLOCK_TIMENS) + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) vd = __arch_get_timens_vdso_data(); tz->tz_minuteswest = vd[CS_HRES_COARSE].tz_minuteswest; @@ -308,7 +308,8 @@ static __maybe_unused __kernel_old_time_t __cvdso_time(__kernel_old_time_t *time const struct vdso_data *vd = __arch_get_vdso_data(); __kernel_old_time_t t; - if (IS_ENABLED(CONFIG_TIME_NS) && vd->clock_mode == VCLOCK_TIMENS) + if (IS_ENABLED(CONFIG_TIME_NS) && + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) vd = __arch_get_timens_vdso_data(); t = READ_ONCE(vd[CS_HRES_COARSE].basetime[CLOCK_REALTIME].sec); @@ -332,7 +333,8 @@ int __cvdso_clock_getres_common(clockid_t clock, struct __kernel_timespec *res) if (unlikely((u32) clock >= MAX_CLOCKS)) return -1; - if (IS_ENABLED(CONFIG_TIME_NS) && vd->clock_mode == VCLOCK_TIMENS) + if (IS_ENABLED(CONFIG_TIME_NS) && + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) vd = __arch_get_timens_vdso_data(); /* From ae12e08539de6717502c2f9f83bd60df939b5c08 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 7 Feb 2020 13:39:02 +0100 Subject: [PATCH 0299/2231] lib/vdso: Allow fixed clock mode Some architectures have a fixed clocksource which is known at compile time and cannot be replaced or disabled at runtime, e.g. timebase on PowerPC. For such cases the clock mode check in the VDSO code is pointless. Move the check for a VDSO capable clocksource into an inline function and allow architectures to redefine it via a macro. [ tglx: Removed the #ifdef mess ] Signed-off-by: Christophe Leroy Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.748756829@linutronix.de --- lib/vdso/gettimeofday.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index a76ac8d17c5f..8eb6d1e9a8ff 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -46,6 +46,13 @@ static inline bool __arch_vdso_hres_capable(void) } #endif +#ifndef vdso_clocksource_ok +static inline bool vdso_clocksource_ok(const struct vdso_data *vd) +{ + return vd->clock_mode != VDSO_CLOCKMODE_NONE; +} +#endif + #ifdef CONFIG_TIME_NS static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk, struct __kernel_timespec *ts) @@ -66,7 +73,7 @@ static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk, do { seq = vdso_read_begin(vd); - if (unlikely(vd->clock_mode == VDSO_CLOCKMODE_NONE)) + if (unlikely(!vdso_clocksource_ok(vd))) return -1; cycles = __arch_get_hw_counter(vd->clock_mode); @@ -134,7 +141,7 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, } smp_rmb(); - if (unlikely(vd->clock_mode == VDSO_CLOCKMODE_NONE)) + if (unlikely(!vdso_clocksource_ok(vd))) return -1; cycles = __arch_get_hw_counter(vd->clock_mode); From 8345228ccf31f94e3ff7ec5458ac7cc13cb323fa Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 7 Feb 2020 13:39:03 +0100 Subject: [PATCH 0300/2231] lib/vdso: Allow architectures to override the ns shift operation On powerpc/32, GCC (8.1) generates pretty bad code for the ns >>= vd->shift operation taking into account that the shift is always <= 32 and the upper part of the result is likely to be zero. GCC makes reversed assumptions considering the shift to be likely >= 32 and the upper part to be like not zero. unsigned long long shift(unsigned long long x, unsigned char s) { return x >> s; } results in: 00000018 : 18: 35 25 ff e0 addic. r9,r5,-32 1c: 41 80 00 10 blt 2c 20: 7c 64 4c 30 srw r4,r3,r9 24: 38 60 00 00 li r3,0 28: 4e 80 00 20 blr 2c: 54 69 08 3c rlwinm r9,r3,1,0,30 30: 21 45 00 1f subfic r10,r5,31 34: 7c 84 2c 30 srw r4,r4,r5 38: 7d 29 50 30 slw r9,r9,r10 3c: 7c 63 2c 30 srw r3,r3,r5 40: 7d 24 23 78 or r4,r9,r4 44: 4e 80 00 20 blr Even when forcing the shift to be smaller than 32 with an &= 31, it still considers the shift as likely >= 32. Move the default shift implementation into an inline which can be redefined in architecture code via a macro. [ tglx: Made the shift argument u32 and removed the __arch prefix ] Signed-off-by: Christophe Leroy Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lore.kernel.org/r/b3d449de856982ed060a71e6ace8eeca4654e685.1580399657.git.christophe.leroy@c-s.fr Link: https://lkml.kernel.org/r/20200207124403.857649978@linutronix.de --- lib/vdso/gettimeofday.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index 8eb6d1e9a8ff..b95aef97501e 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -39,6 +39,13 @@ u64 vdso_calc_delta(u64 cycles, u64 last, u64 mask, u32 mult) } #endif +#ifndef vdso_shift_ns +static __always_inline u64 vdso_shift_ns(u64 ns, u32 shift) +{ + return ns >> shift; +} +#endif + #ifndef __arch_vdso_hres_capable static inline bool __arch_vdso_hres_capable(void) { @@ -80,7 +87,7 @@ static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk, ns = vdso_ts->nsec; last = vd->cycle_last; ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); - ns >>= vd->shift; + ns = vdso_shift_ns(ns, vd->shift); sec = vdso_ts->sec; } while (unlikely(vdso_read_retry(vd, seq))); @@ -148,7 +155,7 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, ns = vdso_ts->nsec; last = vd->cycle_last; ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); - ns >>= vd->shift; + ns = vdso_shift_ns(ns, vd->shift); sec = vdso_ts->sec; } while (unlikely(vdso_read_retry(vd, seq))); From e876f0b69dc993e86ca7795e63e98385aa9a7ef3 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 7 Feb 2020 13:39:04 +0100 Subject: [PATCH 0301/2231] lib/vdso: Allow architectures to provide the vdso data pointer On powerpc, __arch_get_vdso_data() clobbers the link register, requiring the caller to save it. As the parent function already has to set a stack frame and saves the link register before calling the C vdso function, retrieving the vdso data pointer there is less overhead. Split out the functional code from the __cvdso.*() interfaces into new static functions which can either be called from the existing interfaces with the vdso data pointer supplied via __arch_get_vdso_data() or directly from ASM code. Signed-off-by: Christophe Leroy Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lore.kernel.org/r/abf97996602ef07223fec30c005df78e5ed41b2e.1580399657.git.christophe.leroy@c-s.fr Link: https://lkml.kernel.org/r/20200207124403.965789141@linutronix.de --- lib/vdso/gettimeofday.c | 74 +++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index b95aef97501e..72d282ffd156 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -233,9 +233,9 @@ static __always_inline int do_coarse(const struct vdso_data *vd, clockid_t clk, } static __maybe_unused int -__cvdso_clock_gettime_common(clockid_t clock, struct __kernel_timespec *ts) +__cvdso_clock_gettime_common(const struct vdso_data *vd, clockid_t clock, + struct __kernel_timespec *ts) { - const struct vdso_data *vd = __arch_get_vdso_data(); u32 msk; /* Check for negative values or invalid clocks */ @@ -260,23 +260,31 @@ __cvdso_clock_gettime_common(clockid_t clock, struct __kernel_timespec *ts) } static __maybe_unused int -__cvdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts) +__cvdso_clock_gettime_data(const struct vdso_data *vd, clockid_t clock, + struct __kernel_timespec *ts) { - int ret = __cvdso_clock_gettime_common(clock, ts); + int ret = __cvdso_clock_gettime_common(vd, clock, ts); if (unlikely(ret)) return clock_gettime_fallback(clock, ts); return 0; } +static __maybe_unused int +__cvdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts) +{ + return __cvdso_clock_gettime_data(__arch_get_vdso_data(), clock, ts); +} + #ifdef BUILD_VDSO32 static __maybe_unused int -__cvdso_clock_gettime32(clockid_t clock, struct old_timespec32 *res) +__cvdso_clock_gettime32_data(const struct vdso_data *vd, clockid_t clock, + struct old_timespec32 *res) { struct __kernel_timespec ts; int ret; - ret = __cvdso_clock_gettime_common(clock, &ts); + ret = __cvdso_clock_gettime_common(vd, clock, &ts); if (unlikely(ret)) return clock_gettime32_fallback(clock, res); @@ -287,12 +295,18 @@ __cvdso_clock_gettime32(clockid_t clock, struct old_timespec32 *res) return ret; } + +static __maybe_unused int +__cvdso_clock_gettime32(clockid_t clock, struct old_timespec32 *res) +{ + return __cvdso_clock_gettime32_data(__arch_get_vdso_data(), clock, res); +} #endif /* BUILD_VDSO32 */ static __maybe_unused int -__cvdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz) +__cvdso_gettimeofday_data(const struct vdso_data *vd, + struct __kernel_old_timeval *tv, struct timezone *tz) { - const struct vdso_data *vd = __arch_get_vdso_data(); if (likely(tv != NULL)) { struct __kernel_timespec ts; @@ -316,10 +330,16 @@ __cvdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz) return 0; } -#ifdef VDSO_HAS_TIME -static __maybe_unused __kernel_old_time_t __cvdso_time(__kernel_old_time_t *time) +static __maybe_unused int +__cvdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz) +{ + return __cvdso_gettimeofday_data(__arch_get_vdso_data(), tv, tz); +} + +#ifdef VDSO_HAS_TIME +static __maybe_unused __kernel_old_time_t +__cvdso_time_data(const struct vdso_data *vd, __kernel_old_time_t *time) { - const struct vdso_data *vd = __arch_get_vdso_data(); __kernel_old_time_t t; if (IS_ENABLED(CONFIG_TIME_NS) && @@ -333,13 +353,18 @@ static __maybe_unused __kernel_old_time_t __cvdso_time(__kernel_old_time_t *time return t; } + +static __maybe_unused __kernel_old_time_t __cvdso_time(__kernel_old_time_t *time) +{ + return __cvdso_time_data(__arch_get_vdso_data(), time); +} #endif /* VDSO_HAS_TIME */ #ifdef VDSO_HAS_CLOCK_GETRES static __maybe_unused -int __cvdso_clock_getres_common(clockid_t clock, struct __kernel_timespec *res) +int __cvdso_clock_getres_common(const struct vdso_data *vd, clockid_t clock, + struct __kernel_timespec *res) { - const struct vdso_data *vd = __arch_get_vdso_data(); u32 msk; u64 ns; @@ -378,23 +403,31 @@ int __cvdso_clock_getres_common(clockid_t clock, struct __kernel_timespec *res) } static __maybe_unused -int __cvdso_clock_getres(clockid_t clock, struct __kernel_timespec *res) +int __cvdso_clock_getres_data(const struct vdso_data *vd, clockid_t clock, + struct __kernel_timespec *res) { - int ret = __cvdso_clock_getres_common(clock, res); + int ret = __cvdso_clock_getres_common(vd, clock, res); if (unlikely(ret)) return clock_getres_fallback(clock, res); return 0; } +static __maybe_unused +int __cvdso_clock_getres(clockid_t clock, struct __kernel_timespec *res) +{ + return __cvdso_clock_getres_data(__arch_get_vdso_data(), clock, res); +} + #ifdef BUILD_VDSO32 static __maybe_unused int -__cvdso_clock_getres_time32(clockid_t clock, struct old_timespec32 *res) +__cvdso_clock_getres_time32_data(const struct vdso_data *vd, clockid_t clock, + struct old_timespec32 *res) { struct __kernel_timespec ts; int ret; - ret = __cvdso_clock_getres_common(clock, &ts); + ret = __cvdso_clock_getres_common(vd, clock, &ts); if (unlikely(ret)) return clock_getres32_fallback(clock, res); @@ -405,5 +438,12 @@ __cvdso_clock_getres_time32(clockid_t clock, struct old_timespec32 *res) } return ret; } + +static __maybe_unused int +__cvdso_clock_getres_time32(clockid_t clock, struct old_timespec32 *res) +{ + return __cvdso_clock_getres_time32_data(__arch_get_vdso_data(), + clock, res); +} #endif /* BUILD_VDSO32 */ #endif /* VDSO_HAS_CLOCK_GETRES */ From 6e317c32fd39a13e4854a27958d5e35d15d196be Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Sat, 18 Jan 2020 01:59:00 +0300 Subject: [PATCH 0302/2231] timer: Improve the comment describing schedule_timeout() When working commit 6dcd5d7a7a29c1e, a mistake was noticed by Linus: schedule_timeout() was called without setting the task state to anything particular. It calls the scheduler, but doesn't delay anything, because the task stays runnable. That happens because sched_submit_work() does nothing for tasks in TASK_RUNNING state. That turned out to be the intended behavior. Adding a WARN() is not useful as the task could be woken up right after setting the state and before reaching schedule_timeout(). Improve the comment about schedule_timeout() and describe that more explicitly. Signed-off-by: Alexander Popov Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20200117225900.16340-1-alex.popov@linux.com --- kernel/time/timer.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 4820823515e9..cb34fac9d9f7 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1828,21 +1828,23 @@ static void process_timeout(struct timer_list *t) * schedule_timeout - sleep until timeout * @timeout: timeout value in jiffies * - * Make the current task sleep until @timeout jiffies have - * elapsed. The routine will return immediately unless - * the current task state has been set (see set_current_state()). + * Make the current task sleep until @timeout jiffies have elapsed. + * The function behavior depends on the current task state + * (see also set_current_state() description): * - * You can set the task state as follows - + * %TASK_RUNNING - the scheduler is called, but the task does not sleep + * at all. That happens because sched_submit_work() does nothing for + * tasks in %TASK_RUNNING state. * * %TASK_UNINTERRUPTIBLE - at least @timeout jiffies are guaranteed to * pass before the routine returns unless the current task is explicitly - * woken up, (e.g. by wake_up_process())". + * woken up, (e.g. by wake_up_process()). * * %TASK_INTERRUPTIBLE - the routine may return early if a signal is * delivered to the current task or the current task is explicitly woken * up. * - * The current task state is guaranteed to be TASK_RUNNING when this + * The current task state is guaranteed to be %TASK_RUNNING when this * routine returns. * * Specifying a @timeout value of %MAX_SCHEDULE_TIMEOUT will schedule @@ -1850,7 +1852,7 @@ static void process_timeout(struct timer_list *t) * value will be %MAX_SCHEDULE_TIMEOUT. * * Returns 0 when the timer has expired otherwise the remaining time in - * jiffies will be returned. In all cases the return value is guaranteed + * jiffies will be returned. In all cases the return value is guaranteed * to be non-negative. */ signed long __sched schedule_timeout(signed long timeout) From 5fb1c2a5bbf79ccca8d17cf97f66085be5808027 Mon Sep 17 00:00:00 2001 From: Amol Grover Date: Sun, 16 Feb 2020 13:13:30 +0530 Subject: [PATCH 0303/2231] posix-timers: Pass lockdep expression to RCU lists head is traversed using hlist_for_each_entry_rcu outside an RCU read-side critical section but under the protection of hash_lock. Hence, add corresponding lockdep expression to silence false-positive lockdep warnings, and harden RCU lists. [ tglx: Removed the macro and put the condition right where it's used ] Signed-off-by: Amol Grover Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20200216074330.GA14025@workstation-portable --- kernel/time/posix-timers.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index ff0eb30de346..07709ac30439 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -121,7 +121,8 @@ static struct k_itimer *__posix_timers_find(struct hlist_head *head, { struct k_itimer *timer; - hlist_for_each_entry_rcu(timer, head, t_hash) { + hlist_for_each_entry_rcu(timer, head, t_hash, + lockdep_is_held(&hash_lock)) { if ((timer->it_signal == sig) && (timer->it_id == id)) return timer; } From 502cdd605edd95209661c8bf90927af6d05c011c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 12 Feb 2020 23:02:23 +0800 Subject: [PATCH 0304/2231] regulator: mp5416: Fix output discharge enable bit for LDOs The .active_discharge_on/.active_discharge_mask settings does not match the datasheet, fix it. Signed-off-by: Axel Lin Link: https://lore.kernel.org/r/20200212150223.20042-1-axel.lin@ingics.com Signed-off-by: Mark Brown --- drivers/regulator/mp5416.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/regulator/mp5416.c b/drivers/regulator/mp5416.c index 7954ad17249b..67ce1b52a1a1 100644 --- a/drivers/regulator/mp5416.c +++ b/drivers/regulator/mp5416.c @@ -73,7 +73,7 @@ .owner = THIS_MODULE, \ } -#define MP5416LDO(_name, _id) \ +#define MP5416LDO(_name, _id, _dval) \ [MP5416_LDO ## _id] = { \ .id = MP5416_LDO ## _id, \ .name = _name, \ @@ -87,9 +87,9 @@ .vsel_mask = MP5416_MASK_VSET, \ .enable_reg = MP5416_REG_LDO ##_id, \ .enable_mask = MP5416_REGULATOR_EN, \ - .active_discharge_on = BIT(_id), \ + .active_discharge_on = _dval, \ .active_discharge_reg = MP5416_REG_CTL2, \ - .active_discharge_mask = BIT(_id), \ + .active_discharge_mask = _dval, \ .owner = THIS_MODULE, \ } @@ -155,10 +155,10 @@ static struct regulator_desc mp5416_regulators_desc[MP5416_MAX_REGULATORS] = { MP5416BUCK("buck2", 2, mp5416_I_limits2, MP5416_REG_CTL1, BIT(1), 2), MP5416BUCK("buck3", 3, mp5416_I_limits1, MP5416_REG_CTL1, BIT(2), 1), MP5416BUCK("buck4", 4, mp5416_I_limits2, MP5416_REG_CTL2, BIT(5), 2), - MP5416LDO("ldo1", 1), - MP5416LDO("ldo2", 2), - MP5416LDO("ldo3", 3), - MP5416LDO("ldo4", 4), + MP5416LDO("ldo1", 1, BIT(4)), + MP5416LDO("ldo2", 2, BIT(3)), + MP5416LDO("ldo3", 3, BIT(2)), + MP5416LDO("ldo4", 4, BIT(1)), }; /* From 32a9d0545c5da9717395264de9b8e004e4b90893 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sun, 16 Feb 2020 21:40:17 +0000 Subject: [PATCH 0305/2231] spi: spi-mem: Fix typo, s/fallback/falls back "Fallback" in one word is a noun. "Fall back" is two separate words, a verb and an adverb. Use the latter. Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20200216214012.1106658-1-tudor.ambarus@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index e5a46f0eb93b..3c46747bacb1 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -487,7 +487,7 @@ static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc, * This function is creating a direct mapping descriptor which can then be used * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write(). * If the SPI controller driver does not support direct mapping, this function - * fallback to an implementation using spi_mem_exec_op(), so that the caller + * falls back to an implementation using spi_mem_exec_op(), so that the caller * doesn't have to bother implementing a fallback on his own. * * Return: a valid pointer in case of success, and ERR_PTR() otherwise. From de8964995c798fcf372980e5d819eec8cf3e41e1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 14 Feb 2020 15:58:53 +0100 Subject: [PATCH 0306/2231] i3c: master: no need to iterate master device twice We already have the master device in a variable, reuse it. Signed-off-by: Wolfram Sang Acked-by: Vitor Soares Reviewed-by: Geert Uytterhoeven Signed-off-by: Boris Brezillon Link: https://lore.kernel.org/linux-i3c/20200214145853.24762-1-wsa+renesas@sang-engineering.com --- drivers/i3c/master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 7f8f896fa0c3..b56207bbed2b 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1953,7 +1953,7 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, * DEFSLVS command. */ if (boardinfo->base.flags & I2C_CLIENT_TEN) { - dev_err(&master->dev, "I2C device with 10 bit address not supported."); + dev_err(dev, "I2C device with 10 bit address not supported."); return -ENOTSUPP; } From 52cff04a81e9571735976e4d70984e89a7ee3885 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Mon, 17 Feb 2020 08:46:27 -0500 Subject: [PATCH 0307/2231] EDAC/mce_amd: Print !SMCA processor warning only once This warning is output for every virtual CPU in a guest on an EPYC 2 system because kvm doesn't enable SMCA. Once is enough too. [ bp: Massage. ] Signed-off-by: Prarit Bhargava Signed-off-by: Borislav Petkov Link: https://lkml.kernel.org/r/20200217134627.19765-1-prarit@redhat.com --- drivers/edac/mce_amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index ea980c556f2e..8874b7722b2f 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -1239,7 +1239,7 @@ static int __init mce_amd_init(void) case 0x17: case 0x18: - pr_warn("Decoding supported only on Scalable MCA processors.\n"); + pr_warn_once("Decoding supported only on Scalable MCA processors.\n"); return -EINVAL; default: From cd982e6c44a7265e1c7e92de4f696c3b3b376e93 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 18 Feb 2020 11:58:08 +0100 Subject: [PATCH 0308/2231] spi: rspi: Factor out handling of common mode bits Basic SPI features like clock phase/polarity and loopback mode are common to all RSPI variants. Factor them out to reduce duplication. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200218105810.902-2-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 85575d45901c..7a1e6d524f34 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -239,7 +239,7 @@ struct spi_ops { int (*set_config_register)(struct rspi_data *rspi, int access_size); int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer); - u16 mode_bits; + u16 extra_mode_bits; u16 flags; u16 fifo_size; u8 num_hw_ss; @@ -1122,7 +1122,6 @@ static int rspi_remove(struct platform_device *pdev) static const struct spi_ops rspi_ops = { .set_config_register = rspi_set_config_register, .transfer_one = rspi_transfer_one, - .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, .flags = SPI_CONTROLLER_MUST_TX, .fifo_size = 8, .num_hw_ss = 2, @@ -1131,7 +1130,6 @@ static const struct spi_ops rspi_ops = { static const struct spi_ops rspi_rz_ops = { .set_config_register = rspi_rz_set_config_register, .transfer_one = rspi_rz_transfer_one, - .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, .flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX, .fifo_size = 8, /* 8 for TX, 32 for RX */ .num_hw_ss = 1, @@ -1140,8 +1138,7 @@ static const struct spi_ops rspi_rz_ops = { static const struct spi_ops qspi_ops = { .set_config_register = qspi_set_config_register, .transfer_one = qspi_transfer_one, - .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP | - SPI_TX_DUAL | SPI_TX_QUAD | + .extra_mode_bits = SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD, .flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX, .fifo_size = 32, @@ -1258,7 +1255,7 @@ static int rspi_probe(struct platform_device *pdev) ctlr->transfer_one = ops->transfer_one; ctlr->prepare_message = rspi_prepare_message; ctlr->unprepare_message = rspi_unprepare_message; - ctlr->mode_bits = ops->mode_bits; + ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP | ops->extra_mode_bits; ctlr->flags = ops->flags; ctlr->dev.of_node = pdev->dev.of_node; ctlr->use_gpio_descriptors = true; From c046f8fd83a902866ba3b122e9f57ca1c7de898c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 18 Feb 2020 11:58:09 +0100 Subject: [PATCH 0309/2231] spi: rspi: Add support for LSB-first word order All RSPI variants support selecting the word order. Advertize support for LSB-first order, and act upon the flag being set. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200218105810.902-3-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 7a1e6d524f34..aef05f2ac749 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -933,6 +933,8 @@ static int rspi_prepare_message(struct spi_controller *ctlr, rspi->spcmd |= SPCMD_CPOL; if (spi->mode & SPI_CPHA) rspi->spcmd |= SPCMD_CPHA; + if (spi->mode & SPI_LSB_FIRST) + rspi->spcmd |= SPCMD_LSBF; /* Configure slave signal to assert */ rspi->spcmd |= SPCMD_SSLA(spi->cs_gpiod ? rspi->ctlr->unused_native_cs @@ -1255,7 +1257,8 @@ static int rspi_probe(struct platform_device *pdev) ctlr->transfer_one = ops->transfer_one; ctlr->prepare_message = rspi_prepare_message; ctlr->unprepare_message = rspi_unprepare_message; - ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP | ops->extra_mode_bits; + ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST | SPI_LOOP | + ops->extra_mode_bits; ctlr->flags = ops->flags; ctlr->dev.of_node = pdev->dev.of_node; ctlr->use_gpio_descriptors = true; From 874ddbce487f077c46957e44e4115b3d82f62c92 Mon Sep 17 00:00:00 2001 From: Alexandre Ghiti Date: Wed, 19 Feb 2020 01:59:53 -0500 Subject: [PATCH 0310/2231] documentation: vm: Advertise support for pte_special in riscv Risc-V architecture has actually supported pte_special since its merge upstream, simply add this info to the documentation. Signed-off-by: Alexandre Ghiti Signed-off-by: Jonathan Corbet --- Documentation/features/vm/pte_special/arch-support.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/features/vm/pte_special/arch-support.txt b/Documentation/features/vm/pte_special/arch-support.txt index 2dc5df6a1cf5..3d492a34c8ee 100644 --- a/Documentation/features/vm/pte_special/arch-support.txt +++ b/Documentation/features/vm/pte_special/arch-support.txt @@ -23,7 +23,7 @@ | openrisc: | TODO | | parisc: | TODO | | powerpc: | ok | - | riscv: | TODO | + | riscv: | ok | | s390: | ok | | sh: | ok | | sparc: | ok | From 2d5dfb5911cb0eed0a9a91ea404ad963f18e5aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Tue, 18 Feb 2020 17:38:25 +0100 Subject: [PATCH 0311/2231] docs: arm: tcm: Fix a few typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jonathan Corbet --- Documentation/arm/tcm.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/arm/tcm.rst b/Documentation/arm/tcm.rst index effd9c7bc968..b256f9783883 100644 --- a/Documentation/arm/tcm.rst +++ b/Documentation/arm/tcm.rst @@ -4,18 +4,18 @@ ARM TCM (Tightly-Coupled Memory) handling in Linux Written by Linus Walleij -Some ARM SoC:s have a so-called TCM (Tightly-Coupled Memory). +Some ARM SoCs have a so-called TCM (Tightly-Coupled Memory). This is usually just a few (4-64) KiB of RAM inside the ARM processor. -Due to being embedded inside the CPU The TCM has a +Due to being embedded inside the CPU, the TCM has a Harvard-architecture, so there is an ITCM (instruction TCM) and a DTCM (data TCM). The DTCM can not contain any instructions, but the ITCM can actually contain data. The size of DTCM or ITCM is minimum 4KiB so the typical minimum configuration is 4KiB ITCM and 4KiB DTCM. -ARM CPU:s have special registers to read out status, physical +ARM CPUs have special registers to read out status, physical location and size of TCM memories. arch/arm/include/asm/cputype.h defines a CPUID_TCM register that you can read out from the system control coprocessor. Documentation from ARM can be found From fb2511247dc4061fd122d0195838278a4a0b7b59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Tue, 18 Feb 2020 16:02:19 +0100 Subject: [PATCH 0312/2231] docs: Fix path to MTD command line partition parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmdlinepart.c has been moved to drivers/mtd/parsers/. Fixes: a3f12a35c91d ("mtd: parsers: Move CMDLINE parser") Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/kernel-parameters.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index dbc22d684627..47cd55e339a5 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2791,7 +2791,7 @@ ,[,,,,] mtdparts= [MTD] - See drivers/mtd/cmdlinepart.c. + See drivers/mtd/parsers/cmdlinepart.c multitce=off [PPC] This parameter disables the use of the pSeries firmware feature for updating multiple TCE entries From 28926994e5d7f049807119c48bd7b94c2d15fc95 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 13 Feb 2020 09:54:00 +0100 Subject: [PATCH 0313/2231] usb: host: fhci-hcd: annotate PIPE_CONTROL switch case with fallthrough After this was made buildable for something other than PPC32, kbuild starts warning drivers/usb/host/fhci-hcd.c:398:8: warning: this statement may fall through [-Wimplicit-fallthrough=] I don't know this code, but from the construction (initializing size with 0 and explicitly using "size +=" in the PIPE_BULK case) I assume that fallthrough is indeed intended. Reported-by: kbuild test robot Signed-off-by: Rasmus Villemoes Acked-by: Li Yang Link: https://lore.kernel.org/r/20200213085401.27862-1-linux@rasmusvillemoes.dk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/fhci-hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 04733876c9c6..a8e1048278d0 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -396,6 +396,7 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, case PIPE_CONTROL: /* 1 td fro setup,1 for ack */ size = 2; + fallthrough; case PIPE_BULK: /* one td for every 4096 bytes(can be up to 8k) */ size += urb->transfer_buffer_length / 4096; From 10892847de816edf4f8f2151174dc557c4a3f63f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 17 Feb 2020 16:59:54 -0800 Subject: [PATCH 0314/2231] usb-storage: Use const to reduce object data size Make structs const to reduce data size ~20KB. Change function arguments and prototypes as necessary to compile. $ size (x86-64 defconfig pre) text data bss dec hex filename 12281 10948 480 23709 5c9d ./drivers/usb/storage/usb.o 111 10528 8 10647 2997 ./drivers/usb/storage/usual-tables.o $ size (x86-64 defconfig post) text data bss dec hex filename 22809 420 480 23709 5c9d drivers/usb/storage/usb.o 10551 0 0 10551 2937 drivers/usb/storage/usual-tables.o Signed-off-by: Joe Perches Acked-by: Alan Stern Link: https://lore.kernel.org/r/cf13bd2d790ae3afbf5da55ea7bed12e00c5119d.camel@perches.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 10 +++++----- drivers/usb/storage/usb.h | 5 +++-- drivers/usb/storage/usual-tables.c | 6 +++--- include/linux/usb_usual.h | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 9a79cd9762f3..94a64729dc27 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -121,12 +121,12 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); .initFunction = init_function, \ } -static struct us_unusual_dev us_unusual_dev_list[] = { +static const struct us_unusual_dev us_unusual_dev_list[] = { # include "unusual_devs.h" { } /* Terminating entry */ }; -static struct us_unusual_dev for_dynamic_ids = +static const struct us_unusual_dev for_dynamic_ids = USUAL_DEV(USB_SC_SCSI, USB_PR_BULK); #undef UNUSUAL_DEV @@ -583,7 +583,7 @@ EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks); /* Get the unusual_devs entries and the string descriptors */ static int get_device_info(struct us_data *us, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev) + const struct us_unusual_dev *unusual_dev) { struct usb_device *dev = us->pusb_dev; struct usb_interface_descriptor *idesc = @@ -933,7 +933,7 @@ static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev, + const struct us_unusual_dev *unusual_dev, struct scsi_host_template *sht) { struct Scsi_Host *host; @@ -1092,7 +1092,7 @@ static struct scsi_host_template usb_stor_host_template; static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct us_unusual_dev *unusual_dev; + const struct us_unusual_dev *unusual_dev; struct us_data *us; int result; int size; diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 85052cd66839..5850d624cac7 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -93,7 +93,8 @@ struct us_data { struct mutex dev_mutex; /* protect pusb_dev */ struct usb_device *pusb_dev; /* this usb_device */ struct usb_interface *pusb_intf; /* this interface */ - struct us_unusual_dev *unusual_dev; /* device-filter entry */ + const struct us_unusual_dev *unusual_dev; + /* device-filter entry */ unsigned long fflags; /* fixed flags from filter */ unsigned long dflags; /* dynamic atomic bitflags */ unsigned int send_bulk_pipe; /* cached pipe values */ @@ -185,7 +186,7 @@ extern int usb_stor_post_reset(struct usb_interface *iface); extern int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev, + const struct us_unusual_dev *unusual_dev, struct scsi_host_template *sht); extern int usb_stor_probe2(struct us_data *us); extern void usb_stor_disconnect(struct usb_interface *intf); diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c index cfd12e523678..529512827d8f 100644 --- a/drivers/usb/storage/usual-tables.c +++ b/drivers/usb/storage/usual-tables.c @@ -40,7 +40,7 @@ .driver_info = (flags) \ } -struct usb_device_id usb_storage_usb_ids[] = { +const struct usb_device_id usb_storage_usb_ids[] = { # include "unusual_devs.h" { } /* Terminating entry */ }; @@ -68,7 +68,7 @@ struct ignore_entry { .bcdmax = bcdDeviceMax, \ } -static struct ignore_entry ignore_ids[] = { +static const struct ignore_entry ignore_ids[] = { # include "unusual_alauda.h" # include "unusual_cypress.h" # include "unusual_datafab.h" @@ -92,7 +92,7 @@ int usb_usual_ignore_device(struct usb_interface *intf) { struct usb_device *udev; unsigned vid, pid, bcd; - struct ignore_entry *p; + const struct ignore_entry *p; udev = interface_to_usbdev(intf); vid = le16_to_cpu(udev->descriptor.idVendor); diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 000a5954b2e8..4a19ac3f24d0 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -92,6 +92,6 @@ enum { US_DO_ALL_FLAGS }; #include extern int usb_usual_ignore_device(struct usb_interface *intf); -extern struct usb_device_id usb_storage_usb_ids[]; +extern const struct usb_device_id usb_storage_usb_ids[]; #endif /* __LINUX_USB_USUAL_H */ From 24f772409b2426a6545eb0d96b297b8913d70539 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 18 Feb 2020 20:52:07 +0200 Subject: [PATCH 0315/2231] usb: core: Make use of acpi_evaluate_object() status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compiler is not happy about dangling variable: .../core/usb-acpi.c: In function ‘usb_acpi_get_connect_type’: .../core/usb-acpi.c:90:14: warning: variable ‘status’ set but not used [-Wunused-but-set-variable] 90 | acpi_status status; | ^~~~~~ Make use of it by checking the status and bail out in case of error. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200218185207.62527-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb-acpi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 9043d7242d67..99d41dc26e08 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -86,7 +86,7 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle, { enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *upc; + union acpi_object *upc = NULL; acpi_status status; /* @@ -98,11 +98,12 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle, * no connectable, the port would be not used. */ status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); - upc = buffer.pointer; - if (!upc || (upc->type != ACPI_TYPE_PACKAGE) - || upc->package.count != 4) { + if (ACPI_FAILURE(status)) + goto out; + + upc = buffer.pointer; + if (!upc || (upc->type != ACPI_TYPE_PACKAGE) || upc->package.count != 4) goto out; - } if (upc->package.elements[0].integer.value) if (pld->user_visible) From 2b974ee056a2a55e120071d014b31cb330117577 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 18 Feb 2020 21:17:17 +0200 Subject: [PATCH 0316/2231] usb: core: Use ACPI_SUCCESS() at appropriate places Use ACPI_SUCCESS() to replace !ACPI_FAILURE(), this avoids additional operation. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200218191717.73512-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 99d41dc26e08..50b2fc7fcc0e 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -187,7 +187,7 @@ usb_acpi_find_companion_for_port(struct usb_port *port_dev) handle = adev->handle; status = acpi_get_physical_device_location(handle, &pld); - if (!ACPI_FAILURE(status) && pld) { + if (ACPI_SUCCESS(status) && pld) { port_dev->location = USB_ACPI_LOCATION_VALID | pld->group_token << 8 | pld->group_position; port_dev->connect_type = usb_acpi_get_connect_type(handle, pld); From b284ddc36cbe8696604c9e39fa97a7d01d5b80c6 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:44 +0000 Subject: [PATCH 0317/2231] usb: gadget: legacy: gmidi: remove useless cast for driver.name usb_composite_driver name is const char pointer, so it not useful to cast longname (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-2-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/gmidi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index 9eea2d18f2bf..265c392810d7 100644 --- a/drivers/usb/gadget/legacy/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -174,7 +174,7 @@ put: } static struct usb_composite_driver midi_driver = { - .name = (char *) longname, + .name = longname, .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_HIGH, From bab6bac280c6bc21a44f95c5a51f42b9830bb325 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:45 +0000 Subject: [PATCH 0318/2231] usb: gadget: legacy: inode: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast shortname (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-3-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index b47938dff1a2..e3dfc2180555 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1736,7 +1736,7 @@ static struct usb_gadget_driver gadgetfs_driver = { .suspend = gadgetfs_suspend, .driver = { - .name = (char *) shortname, + .name = shortname, }, }; From 511a10910eaf5f4cd6cc3fb57c43438ffcaf7e28 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:46 +0000 Subject: [PATCH 0319/2231] usb: gadget: udc: amd5536udc_pci: remove useless cast for driver.name pci_driver name is const char pointer, so it not useful to cast name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-4-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/amd5536udc_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c index bfd1c9e80a1f..80685e4306f3 100644 --- a/drivers/usb/gadget/udc/amd5536udc_pci.c +++ b/drivers/usb/gadget/udc/amd5536udc_pci.c @@ -202,7 +202,7 @@ MODULE_DEVICE_TABLE(pci, pci_id); /* PCI functions */ static struct pci_driver udc_pci_driver = { - .name = (char *) name, + .name = name, .id_table = pci_id, .probe = udc_pci_probe, .remove = udc_pci_remove, From bd6995334483695f34fab2c57f5e820128a46915 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:47 +0000 Subject: [PATCH 0320/2231] usb: gadget: at91_udc: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast driver_name (which is already const char). Acked-by: Alexandre Belloni Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-5-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/at91_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 1b2b548c59a0..eede5cedacb4 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -2021,7 +2021,7 @@ static struct platform_driver at91_udc_driver = { .suspend = at91udc_suspend, .resume = at91udc_resume, .driver = { - .name = (char *) driver_name, + .name = driver_name, .of_match_table = at91_udc_dt_ids, }, }; From f9a4e699761eb709aadc2cdcdb69998c1fb23aa3 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:48 +0000 Subject: [PATCH 0321/2231] usb: gadget: dummy_hcd: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast driver_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-6-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 4c9d1e49d5ed..6e3e3ebf715f 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1134,7 +1134,7 @@ static struct platform_driver dummy_udc_driver = { .suspend = dummy_udc_suspend, .resume = dummy_udc_resume, .driver = { - .name = (char *) gadget_name, + .name = gadget_name, }, }; @@ -2720,7 +2720,7 @@ static struct platform_driver dummy_hcd_driver = { .suspend = dummy_hcd_suspend, .resume = dummy_hcd_resume, .driver = { - .name = (char *) driver_name, + .name = driver_name, }, }; From 4697bf3f7b84e111a29c726abac0512e1fbe501c Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:49 +0000 Subject: [PATCH 0322/2231] usb: gadget: fotg210-udc: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast udc_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-7-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fotg210-udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index 21f3e6c4e4d6..d6ca50f01985 100644 --- a/drivers/usb/gadget/udc/fotg210-udc.c +++ b/drivers/usb/gadget/udc/fotg210-udc.c @@ -1199,7 +1199,7 @@ err: static struct platform_driver fotg210_driver = { .driver = { - .name = (char *)udc_name, + .name = udc_name, }, .probe = fotg210_udc_probe, .remove = fotg210_udc_remove, From 3620ed300763987cad428abf3fdf64d7ab860d34 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:50 +0000 Subject: [PATCH 0323/2231] usb: gadget: fusb300_udc: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast udc_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-8-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fusb300_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 00e3f66836a9..9af8b415f303 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1507,7 +1507,7 @@ clean_up: static struct platform_driver fusb300_driver = { .remove = fusb300_remove, .driver = { - .name = (char *) udc_name, + .name = udc_name, }, }; From 37a757e31d992d4c7bfaa197b8b6f633cae674d7 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:51 +0000 Subject: [PATCH 0324/2231] usb: gadget: goku_udc: remove useless cast for driver.name pci_driver name is const char pointer, so it not useful to cast driver_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-9-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/goku_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 4a46f661d0e4..91dcb1995c27 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1844,7 +1844,7 @@ static const struct pci_device_id pci_ids[] = { { MODULE_DEVICE_TABLE (pci, pci_ids); static struct pci_driver goku_pci_driver = { - .name = (char *) driver_name, + .name = driver_name, .id_table = pci_ids, .probe = goku_probe, From dcfd72ee653ea73908e95104142ef15ee3fc7f6c Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:52 +0000 Subject: [PATCH 0325/2231] usb: gadget: lpc32xx_udc: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast driver_name (which is already const char). Reviewed-by: Alexandre Belloni Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-10-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/lpc32xx_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index d14b2bb3f67c..cb997b82c008 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3267,7 +3267,7 @@ static struct platform_driver lpc32xx_udc_driver = { .suspend = lpc32xx_udc_suspend, .resume = lpc32xx_udc_resume, .driver = { - .name = (char *) driver_name, + .name = driver_name, .of_match_table = of_match_ptr(lpc32xx_udc_of_match), }, }; From 93bc7363f828cba73986a2d8b0e04d939319d8e5 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:53 +0000 Subject: [PATCH 0326/2231] usb: gadget: m66592-udc: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast udc_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-11-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/m66592-udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index a8288df6aadf..75d16a8902e6 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1691,7 +1691,7 @@ clean_up: static struct platform_driver m66592_driver = { .remove = m66592_remove, .driver = { - .name = (char *) udc_name, + .name = udc_name, }, }; From 8d57a84ae96dc3da42f043e8be59c3502ab9ba11 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:54 +0000 Subject: [PATCH 0327/2231] usb: gadget: net2280: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast driver_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-12-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/net2280.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 1fd1b9186e46..4a815aab8f5b 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3857,7 +3857,7 @@ MODULE_DEVICE_TABLE(pci, pci_ids); /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver net2280_pci_driver = { - .name = (char *) driver_name, + .name = driver_name, .id_table = pci_ids, .probe = net2280_probe, From 676edc2074c9dcf8f58e5e79b82002b5a6aebf2c Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:55 +0000 Subject: [PATCH 0328/2231] usb: gadget: omap_udc: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast driver_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-13-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/omap_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index bd12417996db..bf87c6c0d7f6 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -3001,7 +3001,7 @@ static struct platform_driver udc_driver = { .suspend = omap_udc_suspend, .resume = omap_udc_resume, .driver = { - .name = (char *) driver_name, + .name = driver_name, }, }; From 5803e6e3ead75ac15e89fd96e153e9e168e709b4 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:56 +0000 Subject: [PATCH 0329/2231] usb: gadget: r8a66597-udc: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast udc_name (which is already const char). Reviewed-by: Yoshihiro Shimoda Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-14-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/r8a66597-udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 582a16165ea9..537094b485bf 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1968,7 +1968,7 @@ clean_up2: static struct platform_driver r8a66597_driver = { .remove = r8a66597_remove, .driver = { - .name = (char *) udc_name, + .name = udc_name, }, }; From f7bcff35dcb32ecd7b82da510f650893053f3e28 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:57 +0000 Subject: [PATCH 0330/2231] usb: gadget: renesas_usb3: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast udc_name (which is already const char). Reviewed-by: Yoshihiro Shimoda Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-15-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/renesas_usb3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index c5c3c14df67a..42ae99ad9b25 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2906,7 +2906,7 @@ static struct platform_driver renesas_usb3_driver = { .probe = renesas_usb3_probe, .remove = renesas_usb3_remove, .driver = { - .name = (char *)udc_name, + .name = udc_name, .pm = &renesas_usb3_pm_ops, .of_match_table = of_match_ptr(usb3_of_match), }, From 7cbfeb65f0584ccabee8d52bda47eaee6c074e7e Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:58 +0000 Subject: [PATCH 0331/2231] usb: host: ehci-pci: remove useless cast for driver.name pci_driver name is const char pointer, so it not useful to cast hcd_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-16-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index b0882c13a1d1..1a48ab1bd3b2 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -384,7 +384,7 @@ MODULE_DEVICE_TABLE(pci, pci_ids); /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver ehci_pci_driver = { - .name = (char *) hcd_name, + .name = hcd_name, .id_table = pci_ids, .probe = ehci_pci_probe, From cd3d8cfc322f56488fe8a4fb6246751d32c8df6c Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:32:59 +0000 Subject: [PATCH 0332/2231] usb: host: ohci-pci: remove useless cast for driver.name pci_driver name is const char pointer, so it not useful to cast hcd_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-17-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index f4e13a3fddee..22117a6aeb4a 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -288,7 +288,7 @@ MODULE_DEVICE_TABLE (pci, pci_ids); /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver ohci_pci_driver = { - .name = (char *) hcd_name, + .name = hcd_name, .id_table = pci_ids, .probe = usb_hcd_pci_probe, From bb7458c2f4a90c8ff666f0412d4b11be01e5a9f6 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:33:00 +0000 Subject: [PATCH 0333/2231] usb: host: sl811-hcd: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast hcd_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-18-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 72a34a1eb618..adaf4063690a 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1792,7 +1792,7 @@ struct platform_driver sl811h_driver = { .suspend = sl811h_suspend, .resume = sl811h_resume, .driver = { - .name = (char *) hcd_name, + .name = hcd_name, }, }; EXPORT_SYMBOL(sl811h_driver); From b070022220c9b8f7ea07b6acfc133afcf754599e Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:33:01 +0000 Subject: [PATCH 0334/2231] usb: host: uhci-pci: remove useless cast for driver.name pci_driver name is const char pointer, so it not useful to cast hcd_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-19-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 0fa3d72bae26..957c87efc746 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -294,7 +294,7 @@ static const struct pci_device_id uhci_pci_ids[] = { { MODULE_DEVICE_TABLE(pci, uhci_pci_ids); static struct pci_driver uhci_pci_driver = { - .name = (char *)hcd_name, + .name = hcd_name, .id_table = uhci_pci_ids, .probe = usb_hcd_pci_probe, From c02f1ef68a7fba20346b1fa0791ce7012f0c12fb Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:33:02 +0000 Subject: [PATCH 0335/2231] usb: host: xhci-pci: remove useless cast for driver.name pci_driver name is const char pointer, so it not useful to cast hcd_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-20-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 4917c5b033fa..4d3ea921dd14 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -547,7 +547,7 @@ MODULE_DEVICE_TABLE(pci, pci_ids); /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver xhci_pci_driver = { - .name = (char *) hcd_name, + .name = hcd_name, .id_table = pci_ids, .probe = xhci_pci_probe, From 2f41c8a25feb822203f92e17f3720397805d0cfd Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 18 Feb 2020 19:33:03 +0000 Subject: [PATCH 0336/2231] usb: musb: core: remove useless cast for driver.name device_driver name is const char pointer, so it not useful to cast xx_driver_name (which is already const char). Signed-off-by: Corentin Labbe Link: https://lore.kernel.org/r/1582054383-35760-21-git-send-email-clabbe@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index f616fb489542..d590110539ab 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2945,7 +2945,7 @@ static const struct dev_pm_ops musb_dev_pm_ops = { static struct platform_driver musb_driver = { .driver = { - .name = (char *)musb_driver_name, + .name = musb_driver_name, .bus = &platform_bus_type, .pm = MUSB_DEV_PM_OPS, .dev_groups = musb_groups, From a3cb66a508528e9082cba8303b4f31767e7743a2 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 18 Feb 2020 13:59:16 +0100 Subject: [PATCH 0337/2231] docs: pretty up sysctl/kernel.rst This updates sysctl/kernel.rst to use ReStructured Text more fully: * the list of files is now the table of contents (old entries with no corresponding sections are added as empty sections for now); * code references and commands are formatted as code, except for function names which end up linked to the appropriate documentation; * links are used to point to other documentation and other sections; * tables are used to make lists of values more readable (as already done for some sections); * in heavily-reworked paragraphs, sentences are wrapped individually, to make future diffs easier to read. The first mention of the kernel version is dropped. The second mention, saying that the document is accurate for 2.2, is preserved for now; I will update that once the document really is accurate for a current kernel release. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 969 ++++++++++---------- 1 file changed, 483 insertions(+), 486 deletions(-) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index b08ba4e63291..4872610cc491 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -2,263 +2,187 @@ Documentation for /proc/sys/kernel/ =================================== -kernel version 2.2.10 - Copyright (c) 1998, 1999, Rik van Riel Copyright (c) 2009, Shen Feng -For general info and legal blurb, please look in index.rst. +For general info and legal blurb, please look in :doc:`index`. ------------------------------------------------------------------------------ This file contains documentation for the sysctl files in -/proc/sys/kernel/ and is valid for Linux kernel version 2.2. +``/proc/sys/kernel/`` and is valid for Linux kernel version 2.2. The files in this directory can be used to tune and monitor miscellaneous and general things in the operation of the Linux -kernel. Since some of the files _can_ be used to screw up your +kernel. Since some of the files *can* be used to screw up your system, it is advisable to read both documentation and source before actually making adjustments. Currently, these files might (depending on your configuration) -show up in /proc/sys/kernel: +show up in ``/proc/sys/kernel``: -- acct -- acpi_video_flags -- auto_msgmni -- bootloader_type [ X86 only ] -- bootloader_version [ X86 only ] -- cap_last_cap -- core_pattern -- core_pipe_limit -- core_uses_pid -- ctrl-alt-del -- dmesg_restrict -- domainname -- hostname -- hotplug -- hardlockup_all_cpu_backtrace -- hardlockup_panic -- hung_task_panic -- hung_task_check_count -- hung_task_timeout_secs -- hung_task_check_interval_secs -- hung_task_warnings -- hyperv_record_panic_msg -- kexec_load_disabled -- kptr_restrict -- l2cr [ PPC only ] -- modprobe ==> Documentation/debugging-modules.txt -- modules_disabled -- msg_next_id [ sysv ipc ] -- msgmax -- msgmnb -- msgmni -- nmi_watchdog -- osrelease -- ostype -- overflowgid -- overflowuid -- panic -- panic_on_oops -- panic_on_stackoverflow -- panic_on_unrecovered_nmi -- panic_on_warn -- panic_print -- panic_on_rcu_stall -- perf_cpu_time_max_percent -- perf_event_paranoid -- perf_event_max_stack -- perf_event_mlock_kb -- perf_event_max_contexts_per_stack -- pid_max -- powersave-nap [ PPC only ] -- printk -- printk_delay -- printk_ratelimit -- printk_ratelimit_burst -- pty ==> Documentation/filesystems/devpts.txt -- randomize_va_space -- real-root-dev ==> Documentation/admin-guide/initrd.rst -- reboot-cmd [ SPARC only ] -- rtsig-max -- rtsig-nr -- sched_energy_aware -- seccomp/ ==> Documentation/userspace-api/seccomp_filter.rst -- sem -- sem_next_id [ sysv ipc ] -- sg-big-buff [ generic SCSI device (sg) ] -- shm_next_id [ sysv ipc ] -- shm_rmid_forced -- shmall -- shmmax [ sysv ipc ] -- shmmni -- softlockup_all_cpu_backtrace -- soft_watchdog -- stack_erasing -- stop-a [ SPARC only ] -- sysrq ==> Documentation/admin-guide/sysrq.rst -- sysctl_writes_strict -- tainted ==> Documentation/admin-guide/tainted-kernels.rst -- threads-max -- unknown_nmi_panic -- watchdog -- watchdog_thresh -- version +.. contents:: :local: -acct: -===== +acct +==== -highwater lowwater frequency +:: + + highwater lowwater frequency If BSD-style process accounting is enabled these values control its behaviour. If free space on filesystem where the log lives -goes below % accounting suspends. If free space gets -above % accounting resumes. determines +goes below ``lowwater``% accounting suspends. If free space gets +above ``highwater``% accounting resumes. ``frequency`` determines how often do we check the amount of free space (value is in seconds). Default: -4 2 30 -That is, suspend accounting if there left <= 2% free; resume it -if we got >=4%; consider information about amount of free space -valid for 30 seconds. + +:: + + 4 2 30 + +That is, suspend accounting if free space drops below 2%; resume it +if it increases to at least 4%; consider information about amount of +free space valid for 30 seconds. -acpi_video_flags: -================= +acpi_video_flags +================ -flags - -See Doc*/kernel/power/video.txt, it allows mode of video boot to be -set during run time. +See Documentation/kernel/power/video.txt, it allows mode of video boot +to be set during run time. -auto_msgmni: -============ +auto_msgmni +=========== This variable has no effect and may be removed in future kernel releases. Reading it always returns 0. -Up to Linux 3.17, it enabled/disabled automatic recomputing of msgmni -upon memory add/remove or upon ipc namespace creation/removal. +Up to Linux 3.17, it enabled/disabled automatic recomputing of +`msgmni`_ +upon memory add/remove or upon IPC namespace creation/removal. Echoing "1" into this file enabled msgmni automatic recomputing. -Echoing "0" turned it off. auto_msgmni default value was 1. +Echoing "0" turned it off. The default value was 1. -bootloader_type: -================ - -x86 bootloader identification +bootloader_type (x86 only) +========================== This gives the bootloader type number as indicated by the bootloader, shifted left by 4, and OR'd with the low four bits of the bootloader version. The reason for this encoding is that this used to match the -type_of_loader field in the kernel header; the encoding is kept for +``type_of_loader`` field in the kernel header; the encoding is kept for backwards compatibility. That is, if the full bootloader type number is 0x15 and the full version number is 0x234, this file will contain the value 340 = 0x154. -See the type_of_loader and ext_loader_type fields in -Documentation/x86/boot.rst for additional information. +See the ``type_of_loader`` and ``ext_loader_type`` fields in +:doc:`/x86/boot` for additional information. -bootloader_version: -=================== - -x86 bootloader version +bootloader_version (x86 only) +============================= The complete bootloader version number. In the example above, this file will contain the value 564 = 0x234. -See the type_of_loader and ext_loader_ver fields in -Documentation/x86/boot.rst for additional information. +See the ``type_of_loader`` and ``ext_loader_ver`` fields in +:doc:`/x86/boot` for additional information. -cap_last_cap: -============= +cap_last_cap +============ Highest valid capability of the running kernel. Exports -CAP_LAST_CAP from the kernel. +``CAP_LAST_CAP`` from the kernel. -core_pattern: -============= +core_pattern +============ -core_pattern is used to specify a core dumpfile pattern name. +``core_pattern`` is used to specify a core dumpfile pattern name. * max length 127 characters; default value is "core" -* core_pattern is used as a pattern template for the output filename; - certain string patterns (beginning with '%') are substituted with - their actual values. -* backward compatibility with core_uses_pid: +* ``core_pattern`` is used as a pattern template for the output + filename; certain string patterns (beginning with '%') are + substituted with their actual values. +* backward compatibility with ``core_uses_pid``: - If core_pattern does not include "%p" (default does not) - and core_uses_pid is set, then .PID will be appended to + If ``core_pattern`` does not include "%p" (default does not) + and ``core_uses_pid`` is set, then .PID will be appended to the filename. -* corename format specifiers:: +* corename format specifiers - % '%' is dropped - %% output one '%' - %p pid - %P global pid (init PID namespace) - %i tid - %I global tid (init PID namespace) - %u uid (in initial user namespace) - %g gid (in initial user namespace) - %d dump mode, matches PR_SET_DUMPABLE and - /proc/sys/fs/suid_dumpable - %s signal number - %t UNIX time of dump - %h hostname - %e executable filename (may be shortened) - %E executable path - %c maximum size of core file by resource limit RLIMIT_CORE - % both are dropped + ======== ========================================== + % '%' is dropped + %% output one '%' + %p pid + %P global pid (init PID namespace) + %i tid + %I global tid (init PID namespace) + %u uid (in initial user namespace) + %g gid (in initial user namespace) + %d dump mode, matches ``PR_SET_DUMPABLE`` and + ``/proc/sys/fs/suid_dumpable`` + %s signal number + %t UNIX time of dump + %h hostname + %e executable filename (may be shortened) + %E executable path + %c maximum size of core file by resource limit RLIMIT_CORE + % both are dropped + ======== ========================================== * If the first character of the pattern is a '|', the kernel will treat the rest of the pattern as a command to run. The core dump will be written to the standard input of that program instead of to a file. -core_pipe_limit: -================ +core_pipe_limit +=============== -This sysctl is only applicable when core_pattern is configured to pipe -core files to a user space helper (when the first character of -core_pattern is a '|', see above). When collecting cores via a pipe -to an application, it is occasionally useful for the collecting -application to gather data about the crashing process from its -/proc/pid directory. In order to do this safely, the kernel must wait -for the collecting process to exit, so as not to remove the crashing -processes proc files prematurely. This in turn creates the -possibility that a misbehaving userspace collecting process can block -the reaping of a crashed process simply by never exiting. This sysctl -defends against that. It defines how many concurrent crashing -processes may be piped to user space applications in parallel. If -this value is exceeded, then those crashing processes above that value -are noted via the kernel log and their cores are skipped. 0 is a -special value, indicating that unlimited processes may be captured in -parallel, but that no waiting will take place (i.e. the collecting -process is not guaranteed access to /proc//). This -value defaults to 0. +This sysctl is only applicable when `core_pattern`_ is configured to +pipe core files to a user space helper (when the first character of +``core_pattern`` is a '|', see above). +When collecting cores via a pipe to an application, it is occasionally +useful for the collecting application to gather data about the +crashing process from its ``/proc/pid`` directory. +In order to do this safely, the kernel must wait for the collecting +process to exit, so as not to remove the crashing processes proc files +prematurely. +This in turn creates the possibility that a misbehaving userspace +collecting process can block the reaping of a crashed process simply +by never exiting. +This sysctl defends against that. +It defines how many concurrent crashing processes may be piped to user +space applications in parallel. +If this value is exceeded, then those crashing processes above that +value are noted via the kernel log and their cores are skipped. +0 is a special value, indicating that unlimited processes may be +captured in parallel, but that no waiting will take place (i.e. the +collecting process is not guaranteed access to ``/proc//``). +This value defaults to 0. -core_uses_pid: -============== +core_uses_pid +============= The default coredump filename is "core". By setting -core_uses_pid to 1, the coredump filename becomes core.PID. -If core_pattern does not include "%p" (default does not) -and core_uses_pid is set, then .PID will be appended to +``core_uses_pid`` to 1, the coredump filename becomes core.PID. +If `core_pattern`_ does not include "%p" (default does not) +and ``core_uses_pid`` is set, then .PID will be appended to the filename. -ctrl-alt-del: -============= +ctrl-alt-del +============ When the value in this file is 0, ctrl-alt-del is trapped and -sent to the init(1) program to handle a graceful restart. +sent to the ``init(1)`` program to handle a graceful restart. When, however, the value is > 0, Linux's reaction to a Vulcan Nerve Pinch (tm) will be an immediate reboot, without even syncing its dirty buffers. @@ -270,21 +194,22 @@ Note: to decide what to do with it. -dmesg_restrict: -=============== +dmesg_restrict +============== This toggle indicates whether unprivileged users are prevented -from using dmesg(8) to view messages from the kernel's log buffer. -When dmesg_restrict is set to (0) there are no restrictions. When -dmesg_restrict is set set to (1), users must have CAP_SYSLOG to use -dmesg(8). +from using ``dmesg(8)`` to view messages from the kernel's log +buffer. +When ``dmesg_restrict`` is set to 0 there are no restrictions. +When ``dmesg_restrict`` is set set to 1, users must have +``CAP_SYSLOG`` to use ``dmesg(8)``. -The kernel config option CONFIG_SECURITY_DMESG_RESTRICT sets the -default value of dmesg_restrict. +The kernel config option ``CONFIG_SECURITY_DMESG_RESTRICT`` sets the +default value of ``dmesg_restrict``. -domainname & hostname: -====================== +domainname & hostname +===================== These files can be used to set the NIS/YP domainname and the hostname of your box in exactly the same way as the commands @@ -303,167 +228,192 @@ hostname "darkstar" and DNS (Internet Domain Name Server) domainname "frop.org", not to be confused with the NIS (Network Information Service) or YP (Yellow Pages) domainname. These two domain names are in general different. For a detailed discussion -see the hostname(1) man page. +see the ``hostname(1)`` man page. -hardlockup_all_cpu_backtrace: -============================= +hardlockup_all_cpu_backtrace +============================ This value controls the hard lockup detector behavior when a hard lockup condition is detected as to whether or not to gather further debug information. If enabled, arch-specific all-CPU stack dumping will be initiated. -0: do nothing. This is the default behavior. - -1: on detection capture more debug information. += ============================================ +0 Do nothing. This is the default behavior. +1 On detection capture more debug information. += ============================================ -hardlockup_panic: -================= +hardlockup_panic +================ This parameter can be used to control whether the kernel panics when a hard lockup is detected. - 0 - don't panic on hard lockup - 1 - panic on hard lockup += =========================== +0 Don't panic on hard lockup. +1 Panic on hard lockup. += =========================== -See Documentation/admin-guide/lockup-watchdogs.rst for more information. This can -also be set using the nmi_watchdog kernel parameter. +See :doc:`/admin-guide/lockup-watchdogs` for more information. +This can also be set using the nmi_watchdog kernel parameter. -hotplug: -======== +hotplug +======= Path for the hotplug policy agent. -Default value is "/sbin/hotplug". +Default value is "``/sbin/hotplug``". -hung_task_panic: -================ +hung_task_panic +=============== Controls the kernel's behavior when a hung task is detected. -This file shows up if CONFIG_DETECT_HUNG_TASK is enabled. +This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -0: continue operation. This is the default behavior. - -1: panic immediately. += ================================================= +0 Continue operation. This is the default behavior. +1 Panic immediately. += ================================================= -hung_task_check_count: -====================== +hung_task_check_count +===================== The upper bound on the number of tasks that are checked. -This file shows up if CONFIG_DETECT_HUNG_TASK is enabled. +This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -hung_task_timeout_secs: -======================= +hung_task_timeout_secs +====================== When a task in D state did not get scheduled for more than this value report a warning. -This file shows up if CONFIG_DETECT_HUNG_TASK is enabled. +This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -0: means infinite timeout - no checking done. +0 means infinite timeout, no checking is done. -Possible values to set are in range {0..LONG_MAX/HZ}. +Possible values to set are in range {0:``LONG_MAX``/``HZ``}. -hung_task_check_interval_secs: -============================== +hung_task_check_interval_secs +============================= Hung task check interval. If hung task checking is enabled -(see hung_task_timeout_secs), the check is done every -hung_task_check_interval_secs seconds. -This file shows up if CONFIG_DETECT_HUNG_TASK is enabled. +(see `hung_task_timeout_secs`_), the check is done every +``hung_task_check_interval_secs`` seconds. +This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -0 (default): means use hung_task_timeout_secs as checking interval. -Possible values to set are in range {0..LONG_MAX/HZ}. +0 (default) means use ``hung_task_timeout_secs`` as checking +interval. + +Possible values to set are in range {0:``LONG_MAX``/``HZ``}. -hung_task_warnings: -=================== +hung_task_warnings +================== The maximum number of warnings to report. During a check interval if a hung task is detected, this value is decreased by 1. When this value reaches 0, no more warnings will be reported. -This file shows up if CONFIG_DETECT_HUNG_TASK is enabled. +This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -1: report an infinite number of warnings. -hyperv_record_panic_msg: -======================== +hyperv_record_panic_msg +======================= Controls whether the panic kmsg data should be reported to Hyper-V. -0: do not report panic kmsg data. - -1: report the panic kmsg data. This is the default behavior. += ========================================================= +0 Do not report panic kmsg data. +1 Report the panic kmsg data. This is the default behavior. += ========================================================= -kexec_load_disabled: -==================== +kexec_load_disabled +=================== -A toggle indicating if the kexec_load syscall has been disabled. This -value defaults to 0 (false: kexec_load enabled), but can be set to 1 -(true: kexec_load disabled). Once true, kexec can no longer be used, and -the toggle cannot be set back to false. This allows a kexec image to be -loaded before disabling the syscall, allowing a system to set up (and -later use) an image without it being altered. Generally used together -with the "modules_disabled" sysctl. +A toggle indicating if the ``kexec_load`` syscall has been disabled. +This value defaults to 0 (false: ``kexec_load`` enabled), but can be +set to 1 (true: ``kexec_load`` disabled). +Once true, kexec can no longer be used, and the toggle cannot be set +back to false. +This allows a kexec image to be loaded before disabling the syscall, +allowing a system to set up (and later use) an image without it being +altered. +Generally used together with the `modules_disabled`_ sysctl. -kptr_restrict: -============== +kptr_restrict +============= This toggle indicates whether restrictions are placed on -exposing kernel addresses via /proc and other interfaces. +exposing kernel addresses via ``/proc`` and other interfaces. -When kptr_restrict is set to 0 (the default) the address is hashed before -printing. (This is the equivalent to %p.) +When ``kptr_restrict`` is set to 0 (the default) the address is hashed +before printing. +(This is the equivalent to %p.) -When kptr_restrict is set to (1), kernel pointers printed using the %pK -format specifier will be replaced with 0's unless the user has CAP_SYSLOG -and effective user and group ids are equal to the real ids. This is -because %pK checks are done at read() time rather than open() time, so -if permissions are elevated between the open() and the read() (e.g via -a setuid binary) then %pK will not leak kernel pointers to unprivileged -users. Note, this is a temporary solution only. The correct long-term -solution is to do the permission checks at open() time. Consider removing -world read permissions from files that use %pK, and using dmesg_restrict -to protect against uses of %pK in dmesg(8) if leaking kernel pointer -values to unprivileged users is a concern. +When ``kptr_restrict`` is set to 1, kernel pointers printed using the +%pK format specifier will be replaced with 0s unless the user has +``CAP_SYSLOG`` and effective user and group ids are equal to the real +ids. +This is because %pK checks are done at read() time rather than open() +time, so if permissions are elevated between the open() and the read() +(e.g via a setuid binary) then %pK will not leak kernel pointers to +unprivileged users. +Note, this is a temporary solution only. +The correct long-term solution is to do the permission checks at +open() time. +Consider removing world read permissions from files that use %pK, and +using `dmesg_restrict`_ to protect against uses of %pK in ``dmesg(8)`` +if leaking kernel pointer values to unprivileged users is a concern. -When kptr_restrict is set to (2), kernel pointers printed using -%pK will be replaced with 0's regardless of privileges. +When ``kptr_restrict`` is set to 2, kernel pointers printed using +%pK will be replaced with 0s regardless of privileges. -l2cr: (PPC only) -================ +l2cr (PPC only) +=============== This flag controls the L2 cache of G3 processor boards. If 0, the cache is disabled. Enabled if nonzero. -modules_disabled: -================= +modprobe +======== + +See Documentation/debugging-modules.txt. + + +modules_disabled +================ A toggle value indicating if modules are allowed to be loaded in an otherwise modular kernel. This toggle defaults to off (0), but can be set true (1). Once true, modules can be neither loaded nor unloaded, and the toggle cannot be set back -to false. Generally used with the "kexec_load_disabled" toggle. +to false. Generally used with the `kexec_load_disabled`_ toggle. -msg_next_id, sem_next_id, and shm_next_id: -========================================== +.. _msgmni: + +msgmax, msgmnb, and msgmni +========================== + + +msg_next_id, sem_next_id, and shm_next_id (System V IPC) +======================================================== These three toggles allows to specify desired id for next allocated IPC object: message, semaphore or shared memory respectively. By default they are equal to -1, which means generic allocation logic. -Possible values to set are in range {0..INT_MAX}. +Possible values to set are in range {0:``INT_MAX``}. Notes: 1) kernel doesn't guarantee, that new object will have desired id. So, @@ -473,15 +423,16 @@ Notes: fails, it is undefined if the value remains unmodified or is reset to -1. -nmi_watchdog: -============= +nmi_watchdog +============ This parameter can be used to control the NMI watchdog (i.e. the hard lockup detector) on x86 systems. -0 - disable the hard lockup detector - -1 - enable the hard lockup detector += ================================= +0 Disable the hard lockup detector. +1 Enable the hard lockup detector. += ================================= The hard lockup detector monitors each CPU for its ability to respond to timer interrupts. The mechanism utilizes CPU performance counter registers @@ -493,11 +444,11 @@ in a KVM virtual machine. This default can be overridden by adding:: nmi_watchdog=1 -to the guest kernel command line (see Documentation/admin-guide/kernel-parameters.rst). +to the guest kernel command line (see :doc:`/admin-guide/kernel-parameters`). -numa_balancing: -=============== +numa_balancing +============== Enables/disables automatic page fault based NUMA memory balancing. Memory is moved automatically to nodes @@ -515,9 +466,10 @@ ideally is offset by improved memory locality but there is no universal guarantee. If the target workload is already bound to NUMA nodes then this feature should be disabled. Otherwise, if the system overhead from the feature is too high then the rate the kernel samples for NUMA hinting -faults may be controlled by the numa_balancing_scan_period_min_ms, +faults may be controlled by the `numa_balancing_scan_period_min_ms, numa_balancing_scan_delay_ms, numa_balancing_scan_period_max_ms, -numa_balancing_scan_size_mb, and numa_balancing_settle_count sysctls. +numa_balancing_scan_size_mb`_, and numa_balancing_settle_count sysctls. + numa_balancing_scan_period_min_ms, numa_balancing_scan_delay_ms, numa_balancing_scan_period_max_ms, numa_balancing_scan_size_mb =============================================================================================================================== @@ -543,23 +495,23 @@ workload pattern changes and minimises performance impact due to remote memory accesses. These sysctls control the thresholds for scan delays and the number of pages scanned. -numa_balancing_scan_period_min_ms is the minimum time in milliseconds to +``numa_balancing_scan_period_min_ms`` is the minimum time in milliseconds to scan a tasks virtual memory. It effectively controls the maximum scanning rate for each task. -numa_balancing_scan_delay_ms is the starting "scan delay" used for a task +``numa_balancing_scan_delay_ms`` is the starting "scan delay" used for a task when it initially forks. -numa_balancing_scan_period_max_ms is the maximum time in milliseconds to +``numa_balancing_scan_period_max_ms`` is the maximum time in milliseconds to scan a tasks virtual memory. It effectively controls the minimum scanning rate for each task. -numa_balancing_scan_size_mb is how many megabytes worth of pages are +``numa_balancing_scan_size_mb`` is how many megabytes worth of pages are scanned for a given scan. -osrelease, ostype & version: -============================ +osrelease, ostype & version +=========================== :: @@ -570,15 +522,16 @@ osrelease, ostype & version: # cat version #5 Wed Feb 25 21:49:24 MET 1998 -The files osrelease and ostype should be clear enough. Version +The files ``osrelease`` and ``ostype`` should be clear enough. +``version`` needs a little more clarification however. The '#5' means that this is the fifth kernel built from this source base and the date behind it indicates the time the kernel was built. The only way to tune these values is to rebuild the kernel :-) -overflowgid & overflowuid: -========================== +overflowgid & overflowuid +========================= if your architecture did not always support 32-bit UIDs (i.e. arm, i386, m68k, sh, and sparc32), a fixed UID and GID will be returned to @@ -589,108 +542,113 @@ These sysctls allow you to change the value of the fixed UID and GID. The default is 65534. -panic: -====== +panic +===== The value in this file represents the number of seconds the kernel waits before rebooting on a panic. When you use the software watchdog, the recommended setting is 60. -panic_on_io_nmi: -================ +panic_on_io_nmi +=============== Controls the kernel's behavior when a CPU receives an NMI caused by an IO error. -0: try to continue operation (default) - -1: panic immediately. The IO error triggered an NMI. This indicates a - serious system condition which could result in IO data corruption. - Rather than continuing, panicking might be a better choice. Some - servers issue this sort of NMI when the dump button is pushed, - and you can use this option to take a crash dump. += ================================================================== +0 Try to continue operation (default). +1 Panic immediately. The IO error triggered an NMI. This indicates a + serious system condition which could result in IO data corruption. + Rather than continuing, panicking might be a better choice. Some + servers issue this sort of NMI when the dump button is pushed, + and you can use this option to take a crash dump. += ================================================================== -panic_on_oops: -============== +panic_on_oops +============= Controls the kernel's behaviour when an oops or BUG is encountered. -0: try to continue operation - -1: panic immediately. If the `panic` sysctl is also non-zero then the - machine will be rebooted. += =================================================================== +0 Try to continue operation. +1 Panic immediately. If the `panic` sysctl is also non-zero then the + machine will be rebooted. += =================================================================== -panic_on_stackoverflow: -======================= +panic_on_stackoverflow +====================== Controls the kernel's behavior when detecting the overflows of kernel, IRQ and exception stacks except a user stack. -This file shows up if CONFIG_DEBUG_STACKOVERFLOW is enabled. +This file shows up if ``CONFIG_DEBUG_STACKOVERFLOW`` is enabled. -0: try to continue operation. - -1: panic immediately. += ========================== +0 Try to continue operation. +1 Panic immediately. += ========================== -panic_on_unrecovered_nmi: -========================= +panic_on_unrecovered_nmi +======================== The default Linux behaviour on an NMI of either memory or unknown is to continue operation. For many environments such as scientific computing it is preferable that the box is taken out and the error dealt with than an uncorrected parity/ECC error get propagated. -A small number of systems do generate NMI's for bizarre random reasons +A small number of systems do generate NMIs for bizarre random reasons such as power management so the default is off. That sysctl works like the existing panic controls already in that directory. -panic_on_warn: -============== +panic_on_warn +============= Calls panic() in the WARN() path when set to 1. This is useful to avoid a kernel rebuild when attempting to kdump at the location of a WARN(). -0: only WARN(), default behaviour. - -1: call panic() after printing out WARN() location. += ================================================ +0 Only WARN(), default behaviour. +1 Call panic() after printing out WARN() location. += ================================================ -panic_print: -============ +panic_print +=========== Bitmask for printing system info when panic happens. User can chose combination of the following bits: -===== ======================================== +===== ============================================ bit 0 print all tasks info bit 1 print system memory info bit 2 print timer info -bit 3 print locks info if CONFIG_LOCKDEP is on +bit 3 print locks info if ``CONFIG_LOCKDEP`` is on bit 4 print ftrace buffer -===== ======================================== +===== ============================================ So for example to print tasks and memory info on panic, user can:: echo 3 > /proc/sys/kernel/panic_print -panic_on_rcu_stall: -=================== +panic_on_rcu_stall +================== When set to 1, calls panic() after RCU stall detection messages. This is useful to define the root cause of RCU stalls using a vmcore. -0: do not panic() when RCU stall takes place, default behavior. - -1: panic() after printing RCU stall messages. += ============================================================ +0 Do not panic() when RCU stall takes place, default behavior. +1 panic() after printing RCU stall messages. += ============================================================ -perf_cpu_time_max_percent: -========================== +perf_cpu_time_max_percent +========================= Hints to the kernel how much CPU time it should be allowed to use to handle perf sampling events. If the perf subsystem @@ -703,171 +661,179 @@ unexpectedly take too long to execute, the NMIs can become stacked up next to each other so much that nothing else is allowed to execute. -0: - disable the mechanism. Do not monitor or correct perf's - sampling rate no matter how CPU time it takes. +===== ======================================================== +0 Disable the mechanism. Do not monitor or correct perf's + sampling rate no matter how CPU time it takes. -1-100: - attempt to throttle perf's sample rate to this - percentage of CPU. Note: the kernel calculates an - "expected" length of each sample event. 100 here means - 100% of that expected length. Even if this is set to - 100, you may still see sample throttling if this - length is exceeded. Set to 0 if you truly do not care - how much CPU is consumed. +1-100 Attempt to throttle perf's sample rate to this + percentage of CPU. Note: the kernel calculates an + "expected" length of each sample event. 100 here means + 100% of that expected length. Even if this is set to + 100, you may still see sample throttling if this + length is exceeded. Set to 0 if you truly do not care + how much CPU is consumed. +===== ======================================================== -perf_event_paranoid: -==================== +perf_event_paranoid +=================== Controls use of the performance events system by unprivileged users (without CAP_SYS_ADMIN). The default value is 2. === ================================================================== - -1 Allow use of (almost) all events by all users + -1 Allow use of (almost) all events by all users. - Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK + Ignore mlock limit after perf_event_mlock_kb without + ``CAP_IPC_LOCK``. ->=0 Disallow ftrace function tracepoint by users without CAP_SYS_ADMIN +>=0 Disallow ftrace function tracepoint by users without + ``CAP_SYS_ADMIN``. - Disallow raw tracepoint access by users without CAP_SYS_ADMIN + Disallow raw tracepoint access by users without ``CAP_SYS_ADMIN``. ->=1 Disallow CPU event access by users without CAP_SYS_ADMIN +>=1 Disallow CPU event access by users without ``CAP_SYS_ADMIN``. ->=2 Disallow kernel profiling by users without CAP_SYS_ADMIN +>=2 Disallow kernel profiling by users without ``CAP_SYS_ADMIN``. === ================================================================== -perf_event_max_stack: -===================== +perf_event_max_stack +==================== -Controls maximum number of stack frames to copy for (attr.sample_type & -PERF_SAMPLE_CALLCHAIN) configured events, for instance, when using -'perf record -g' or 'perf trace --call-graph fp'. +Controls maximum number of stack frames to copy for (``attr.sample_type & +PERF_SAMPLE_CALLCHAIN``) configured events, for instance, when using +'``perf record -g``' or '``perf trace --call-graph fp``'. This can only be done when no events are in use that have callchains -enabled, otherwise writing to this file will return -EBUSY. +enabled, otherwise writing to this file will return ``-EBUSY``. The default value is 127. -perf_event_mlock_kb: -==================== +perf_event_mlock_kb +=================== Control size of per-cpu ring buffer not counted agains mlock limit. The default value is 512 + 1 page -perf_event_max_contexts_per_stack: -================================== +perf_event_max_contexts_per_stack +================================= Controls maximum number of stack frame context entries for -(attr.sample_type & PERF_SAMPLE_CALLCHAIN) configured events, for -instance, when using 'perf record -g' or 'perf trace --call-graph fp'. +(``attr.sample_type & PERF_SAMPLE_CALLCHAIN``) configured events, for +instance, when using '``perf record -g``' or '``perf trace --call-graph fp``'. This can only be done when no events are in use that have callchains -enabled, otherwise writing to this file will return -EBUSY. +enabled, otherwise writing to this file will return ``-EBUSY``. The default value is 8. -pid_max: -======== +pid_max +======= PID allocation wrap value. When the kernel's next PID value reaches this value, it wraps back to a minimum PID value. -PIDs of value pid_max or larger are not allocated. +PIDs of value ``pid_max`` or larger are not allocated. -ns_last_pid: -============ +ns_last_pid +=========== The last pid allocated in the current (the one task using this sysctl lives in) pid namespace. When selecting a pid for a next task on fork kernel tries to allocate a number starting from this one. -powersave-nap: (PPC only) -========================= +powersave-nap (PPC only) +======================== If set, Linux-PPC will use the 'nap' mode of powersaving, otherwise the 'doze' mode will be used. + ============================================================== -printk: -======= +printk +====== -The four values in printk denote: console_loglevel, -default_message_loglevel, minimum_console_loglevel and -default_console_loglevel respectively. +The four values in printk denote: ``console_loglevel``, +``default_message_loglevel``, ``minimum_console_loglevel`` and +``default_console_loglevel`` respectively. These values influence printk() behavior when printing or -logging error messages. See 'man 2 syslog' for more info on +logging error messages. See '``man 2 syslog``' for more info on the different loglevels. -- console_loglevel: - messages with a higher priority than - this will be printed to the console -- default_message_loglevel: - messages without an explicit priority - will be printed with this priority -- minimum_console_loglevel: - minimum (highest) value to which - console_loglevel can be set -- default_console_loglevel: - default value for console_loglevel +======================== ===================================== +console_loglevel messages with a higher priority than + this will be printed to the console +default_message_loglevel messages without an explicit priority + will be printed with this priority +minimum_console_loglevel minimum (highest) value to which + console_loglevel can be set +default_console_loglevel default value for console_loglevel +======================== ===================================== -printk_delay: -============= +printk_delay +============ -Delay each printk message in printk_delay milliseconds +Delay each printk message in ``printk_delay`` milliseconds Value from 0 - 10000 is allowed. -printk_ratelimit: -================= +printk_ratelimit +================ -Some warning messages are rate limited. printk_ratelimit specifies +Some warning messages are rate limited. ``printk_ratelimit`` specifies the minimum length of time between these messages (in seconds). The default value is 5 seconds. A value of 0 will disable rate limiting. -printk_ratelimit_burst: -======================= +printk_ratelimit_burst +====================== -While long term we enforce one message per printk_ratelimit +While long term we enforce one message per `printk_ratelimit`_ seconds, we do allow a burst of messages to pass through. -printk_ratelimit_burst specifies the number of messages we can +``printk_ratelimit_burst`` specifies the number of messages we can send before ratelimiting kicks in. The default value is 10 messages. -printk_devkmsg: -=============== +printk_devkmsg +============== -Control the logging to /dev/kmsg from userspace: +Control the logging to ``/dev/kmsg`` from userspace: -ratelimit: - default, ratelimited +========= ============================================= +ratelimit default, ratelimited +on unlimited logging to /dev/kmsg from userspace +off logging to /dev/kmsg disabled +========= ============================================= -on: unlimited logging to /dev/kmsg from userspace - -off: logging to /dev/kmsg disabled - -The kernel command line parameter printk.devkmsg= overrides this and is +The kernel command line parameter ``printk.devkmsg=`` overrides this and is a one-time setting until next reboot: once set, it cannot be changed by this sysctl interface anymore. +============================================================== -randomize_va_space: -=================== + +pty +=== + +See Documentation/filesystems/devpts.txt. + + +randomize_va_space +================== This option can be used to select the type of process address space randomization that is used in the system, for architectures @@ -882,10 +848,10 @@ that support this feature. This, among other things, implies that shared libraries will be loaded to random addresses. Also for PIE-linked binaries, the location of code start is randomized. This is the default if the - CONFIG_COMPAT_BRK option is enabled. + ``CONFIG_COMPAT_BRK`` option is enabled. 2 Additionally enable heap randomization. This is the default if - CONFIG_COMPAT_BRK is disabled. + ``CONFIG_COMPAT_BRK`` is disabled. There are a few legacy applications out there (such as some ancient versions of libc.so.5 from 1996) that assume that brk area starts @@ -895,21 +861,27 @@ that support this feature. systems it is safe to choose full randomization. Systems with ancient and/or broken binaries should be configured - with CONFIG_COMPAT_BRK enabled, which excludes the heap from process + with ``CONFIG_COMPAT_BRK`` enabled, which excludes the heap from process address space randomization. == =========================================================================== -reboot-cmd: (Sparc only) -======================== +real-root-dev +============= + +See :doc:`/admin-guide/initrd`. + + +reboot-cmd (SPARC only) +======================= ??? This seems to be a way to give an argument to the Sparc ROM/Flash boot loader. Maybe to tell it what to do after rebooting. ??? -rtsig-max & rtsig-nr: -===================== +rtsig-max & rtsig-nr +==================== The file rtsig-max can be used to tune the maximum number of POSIX realtime (queued) signals that can be outstanding @@ -918,8 +890,8 @@ in the system. rtsig-nr shows the number of RT signals currently queued. -sched_energy_aware: -=================== +sched_energy_aware +================== Enables/disables Energy Aware Scheduling (EAS). EAS starts automatically on platforms where it can run (that is, @@ -929,75 +901,85 @@ requirements for EAS but you do not want to use it, change this value to 0. -sched_schedstats: -================= +sched_schedstats +================ Enables/disables scheduler statistics. Enabling this feature incurs a small amount of overhead in the scheduler but is useful for debugging and performance tuning. -sg-big-buff: -============ +seccomp +======= + +See :doc:`/userspace-api/seccomp_filter`. + + +sg-big-buff +=========== This file shows the size of the generic SCSI (sg) buffer. You can't tune it just yet, but you could change it on -compile time by editing include/scsi/sg.h and changing -the value of SG_BIG_BUFF. +compile time by editing ``include/scsi/sg.h`` and changing +the value of ``SG_BIG_BUFF``. There shouldn't be any reason to change this value. If you can come up with one, you probably know what you are doing anyway :) -shmall: -======= +shmall +====== This parameter sets the total amount of shared memory pages that -can be used system wide. Hence, SHMALL should always be at least -ceil(shmmax/PAGE_SIZE). +can be used system wide. Hence, ``shmall`` should always be at least +``ceil(shmmax/PAGE_SIZE)``. -If you are not sure what the default PAGE_SIZE is on your Linux -system, you can run the following command: +If you are not sure what the default ``PAGE_SIZE`` is on your Linux +system, you can run the following command:: # getconf PAGE_SIZE -shmmax: -======= +shmmax +====== This value can be used to query and set the run time limit on the maximum shared memory segment size that can be created. Shared memory segments up to 1Gb are now supported in the -kernel. This value defaults to SHMMAX. +kernel. This value defaults to ``SHMMAX``. -shm_rmid_forced: -================ +shmmni +====== + + +shm_rmid_forced +=============== Linux lets you set resource limits, including how much memory one -process can consume, via setrlimit(2). Unfortunately, shared memory +process can consume, via ``setrlimit(2)``. Unfortunately, shared memory segments are allowed to exist without association with any process, and thus might not be counted against any resource limits. If enabled, shared memory segments are automatically destroyed when their attach count becomes zero after a detach or a process termination. It will also destroy segments that were created, but never attached to, on exit -from the process. The only use left for IPC_RMID is to immediately +from the process. The only use left for ``IPC_RMID`` is to immediately destroy an unattached segment. Of course, this breaks the way things are defined, so some applications might stop working. Note that this feature will do you no good unless you also configure your resource -limits (in particular, RLIMIT_AS and RLIMIT_NPROC). Most systems don't +limits (in particular, ``RLIMIT_AS`` and ``RLIMIT_NPROC``). Most systems don't need this. Note that if you change this from 0 to 1, already created segments without users and with a dead originative process will be destroyed. -sysctl_writes_strict: -===================== +sysctl_writes_strict +==================== Control how file position affects the behavior of updating sysctl values -via the /proc/sys interface: +via the ``/proc/sys`` interface: == ====================================================================== -1 Legacy per-write sysctl value handling, with no printk warnings. @@ -1014,8 +996,8 @@ via the /proc/sys interface: == ====================================================================== -softlockup_all_cpu_backtrace: -============================= +softlockup_all_cpu_backtrace +============================ This value controls the soft lockup detector thread's behavior when a soft lockup condition is detected as to whether or not @@ -1025,43 +1007,56 @@ be issued an NMI and instructed to capture stack trace. This feature is only applicable for architectures which support NMI. -0: do nothing. This is the default behavior. - -1: on detection capture more debug information. += ============================================ +0 Do nothing. This is the default behavior. +1 On detection capture more debug information. += ============================================ -soft_watchdog: -============== +soft_watchdog +============= This parameter can be used to control the soft lockup detector. - 0 - disable the soft lockup detector - - 1 - enable the soft lockup detector += ================================= +0 Disable the soft lockup detector. +1 Enable the soft lockup detector. += ================================= The soft lockup detector monitors CPUs for threads that are hogging the CPUs without rescheduling voluntarily, and thus prevent the 'watchdog/N' threads from running. The mechanism depends on the CPUs ability to respond to timer interrupts which are needed for the 'watchdog/N' threads to be woken up by -the watchdog timer function, otherwise the NMI watchdog - if enabled - can +the watchdog timer function, otherwise the NMI watchdog — if enabled — can detect a hard lockup condition. -stack_erasing: -============== +stack_erasing +============= This parameter can be used to control kernel stack erasing at the end -of syscalls for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK. +of syscalls for kernels built with ``CONFIG_GCC_PLUGIN_STACKLEAK``. That erasing reduces the information which kernel stack leak bugs can reveal and blocks some uninitialized stack variable attacks. The tradeoff is the performance impact: on a single CPU system kernel compilation sees a 1% slowdown, other systems and workloads may vary. - 0: kernel stack erasing is disabled, STACKLEAK_METRICS are not updated. += ==================================================================== +0 Kernel stack erasing is disabled, STACKLEAK_METRICS are not updated. +1 Kernel stack erasing is enabled (default), it is performed before + returning to the userspace at the end of syscalls. += ==================================================================== - 1: kernel stack erasing is enabled (default), it is performed before - returning to the userspace at the end of syscalls. + +stop-a (SPARC only) +=================== + + +sysrq +===== + +See :doc:`/admin-guide/sysrq`. tainted @@ -1091,30 +1086,30 @@ ORed together. The letters are seen in "Tainted" line of Oops reports. 131072 `(T)` The kernel was built with the struct randomization plugin ====== ===== ============================================================== -See Documentation/admin-guide/tainted-kernels.rst for more information. +See :doc:`/admin-guide/tainted-kernels` for more information. -threads-max: -============ +threads-max +=========== This value controls the maximum number of threads that can be created -using fork(). +using ``fork()``. During initialization the kernel sets this value such that even if the maximum number of threads is created, the thread structures occupy only a part (1/8th) of the available RAM pages. -The minimum value that can be written to threads-max is 1. +The minimum value that can be written to ``threads-max`` is 1. -The maximum value that can be written to threads-max is given by the -constant FUTEX_TID_MASK (0x3fffffff). +The maximum value that can be written to ``threads-max`` is given by the +constant ``FUTEX_TID_MASK`` (0x3fffffff). -If a value outside of this range is written to threads-max an error -EINVAL occurs. +If a value outside of this range is written to ``threads-max`` an +``EINVAL`` error occurs. -unknown_nmi_panic: -================== +unknown_nmi_panic +================= The value in this file affects behavior of handling NMI. When the value is non-zero, unknown NMI is trapped and then panic occurs. At @@ -1124,37 +1119,39 @@ NMI switch that most IA32 servers have fires unknown NMI up, for example. If a system hangs up, try pressing the NMI switch. -watchdog: -========= +watchdog +======== This parameter can be used to disable or enable the soft lockup detector -_and_ the NMI watchdog (i.e. the hard lockup detector) at the same time. +*and* the NMI watchdog (i.e. the hard lockup detector) at the same time. - 0 - disable both lockup detectors - - 1 - enable both lockup detectors += ============================== +0 Disable both lockup detectors. +1 Enable both lockup detectors. += ============================== The soft lockup detector and the NMI watchdog can also be disabled or -enabled individually, using the soft_watchdog and nmi_watchdog parameters. -If the watchdog parameter is read, for example by executing:: +enabled individually, using the ``soft_watchdog`` and ``nmi_watchdog`` +parameters. +If the ``watchdog`` parameter is read, for example by executing:: cat /proc/sys/kernel/watchdog -the output of this command (0 or 1) shows the logical OR of soft_watchdog -and nmi_watchdog. +the output of this command (0 or 1) shows the logical OR of +``soft_watchdog`` and ``nmi_watchdog``. -watchdog_cpumask: -================= +watchdog_cpumask +================ This value can be used to control on which cpus the watchdog may run. -The default cpumask is all possible cores, but if NO_HZ_FULL is +The default cpumask is all possible cores, but if ``NO_HZ_FULL`` is enabled in the kernel config, and cores are specified with the -nohz_full= boot argument, those cores are excluded by default. +``nohz_full=`` boot argument, those cores are excluded by default. Offline cores can be included in this mask, and if the core is later brought online, the watchdog will be started based on the mask value. -Typically this value would only be touched in the nohz_full case +Typically this value would only be touched in the ``nohz_full`` case to re-enable cores that by default were not running the watchdog, if a kernel lockup was suspected on those cores. @@ -1165,12 +1162,12 @@ might say:: echo 0,2-4 > /proc/sys/kernel/watchdog_cpumask -watchdog_thresh: -================ +watchdog_thresh +=============== This value can be used to control the frequency of hrtimer and NMI events and the soft and hard lockup thresholds. The default threshold is 10 seconds. -The softlockup threshold is (2 * watchdog_thresh). Setting this +The softlockup threshold is (``2 * watchdog_thresh``). Setting this tunable to zero will disable lockup detection altogether. From 0317c5371e6a9b71a2e25b47013dd5c62d55d1a6 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 18 Feb 2020 13:59:17 +0100 Subject: [PATCH 0338/2231] docs: merge debugging-modules.txt into sysctl/kernel.rst This fits nicely in sysctl/kernel.rst, merge it (and rephrase it) instead of linking to it. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 14 ++++++++++++- Documentation/debugging-modules.txt | 22 --------------------- 2 files changed, 13 insertions(+), 23 deletions(-) delete mode 100644 Documentation/debugging-modules.txt diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 4872610cc491..bb56ff25d947 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -387,7 +387,19 @@ This flag controls the L2 cache of G3 processor boards. If modprobe ======== -See Documentation/debugging-modules.txt. +This gives the full path of the modprobe command which the kernel will +use to load modules. This can be used to debug module loading +requests:: + + echo '#! /bin/sh' > /tmp/modprobe + echo 'echo "$@" >> /tmp/modprobe.log' >> /tmp/modprobe + echo 'exec /sbin/modprobe "$@"' >> /tmp/modprobe + chmod a+x /tmp/modprobe + echo /tmp/modprobe > /proc/sys/kernel/modprobe + +This only applies when the *kernel* is requesting that the module be +loaded; it won't have any effect if the module is being loaded +explicitly using ``modprobe`` from userspace. modules_disabled diff --git a/Documentation/debugging-modules.txt b/Documentation/debugging-modules.txt deleted file mode 100644 index 172ad4aec493..000000000000 --- a/Documentation/debugging-modules.txt +++ /dev/null @@ -1,22 +0,0 @@ -Debugging Modules after 2.6.3 ------------------------------ - -In almost all distributions, the kernel asks for modules which don't -exist, such as "net-pf-10" or whatever. Changing "modprobe -q" to -"succeed" in this case is hacky and breaks some setups, and also we -want to know if it failed for the fallback code for old aliases in -fs/char_dev.c, for example. - -In the past a debugging message which would fill people's logs was -emitted. This debugging message has been removed. The correct way -of debugging module problems is something like this: - -echo '#! /bin/sh' > /tmp/modprobe -echo 'echo "$@" >> /tmp/modprobe.log' >> /tmp/modprobe -echo 'exec /sbin/modprobe "$@"' >> /tmp/modprobe -chmod a+x /tmp/modprobe -echo /tmp/modprobe > /proc/sys/kernel/modprobe - -Note that the above applies only when the *kernel* is requesting -that the module be loaded -- it won't have any effect if that module -is being loaded explicitly using "modprobe" from userspace. From a474105bb6a6fe85ea30d7fe0a087184da32c751 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 18 Feb 2020 13:59:18 +0100 Subject: [PATCH 0339/2231] docs: drop l2cr from sysctl/kernel.rst The l2cr sysctl entry was removed in commit c2f3dabefa73 ("sysctl: kill binary sysctl KERN_PPC_L2CR"), this removes the corresponding documentation. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index bb56ff25d947..99569a26f93e 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -377,13 +377,6 @@ When ``kptr_restrict`` is set to 2, kernel pointers printed using %pK will be replaced with 0s regardless of privileges. -l2cr (PPC only) -=============== - -This flag controls the L2 cache of G3 processor boards. If -0, the cache is disabled. Enabled if nonzero. - - modprobe ======== From fa5b526411bb5afe7736ce14bab18c0b68db4251 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 18 Feb 2020 13:59:19 +0100 Subject: [PATCH 0340/2231] docs: add missing IPC documentation in sysctl/kernel.rst This adds short descriptions of msgmax, msgmnb, msgmni, and shmmni, which were previously listed in kernel.rst but not described. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 99569a26f93e..0ae52156db75 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -410,6 +410,15 @@ to false. Generally used with the `kexec_load_disabled`_ toggle. msgmax, msgmnb, and msgmni ========================== +``msgmax`` is the maximum size of an IPC message, in bytes. 8192 by +default (``MSGMAX``). + +``msgmnb`` is the maximum size of an IPC queue, in bytes. 16384 by +default (``MSGMNB``). + +``msgmni`` is the maximum number of IPC queues. 32000 by default +(``MSGMNI``). + msg_next_id, sem_next_id, and shm_next_id (System V IPC) ======================================================== @@ -958,6 +967,9 @@ kernel. This value defaults to ``SHMMAX``. shmmni ====== +This value determines the maximum number of shared memory segments. +4096 by default (``SHMMNI``). + shm_rmid_forced =============== From a1ad4f15054b58636aa58f0df2961259f8781746 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 18 Feb 2020 13:59:20 +0100 Subject: [PATCH 0341/2231] docs: document stop-a in sysctl/kernel.rst This describes the SPARC-specific stop-a sysctl entry, which was previously listed in kernel.rst but not documented. Base on the implementation in arch/sparc/kernel/setup_{32,64}.c and kernel/panic.c. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 0ae52156db75..3cbbe4502e18 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -1069,6 +1069,16 @@ compilation sees a 1% slowdown, other systems and workloads may vary. stop-a (SPARC only) =================== +Controls Stop-A: + += ==================================== +0 Stop-A has no effect. +1 Stop-A breaks to the PROM (default). += ==================================== + +Stop-A is always enabled on a panic, so that the user can return to +the boot PROM. + sysrq ===== From 404347e68aeb81b89dc440135ed23fcabff104f9 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 18 Feb 2020 13:59:21 +0100 Subject: [PATCH 0342/2231] docs: document panic fully in sysctl/kernel.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The description of panic doesn’t cover all the supported scenarios; this patch fixes that, describing the three possibilities (no reboot, immediate reboot, reboot after a delay). Based on the implementation in kernel/panic.c. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 3cbbe4502e18..60c97a79ff26 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -559,9 +559,15 @@ The default is 65534. panic ===== -The value in this file represents the number of seconds the kernel -waits before rebooting on a panic. When you use the software watchdog, -the recommended setting is 60. +The value in this file determines the behaviour of the kernel on a +panic: + +* if zero, the kernel will loop forever; +* if negative, the kernel will reboot immediately; +* if positive, the kernel will reboot after the corresponding number + of seconds. + +When you use the software watchdog, the recommended setting is 60. panic_on_io_nmi From 8f21f54b8a9517e0213948088aca757a0f122447 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 18 Feb 2020 13:59:23 +0100 Subject: [PATCH 0343/2231] docs: sysctl/kernel: remove rtsig entries These have no corresponding code in the kernel. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 60c97a79ff26..6c0d8c55101c 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -900,16 +900,6 @@ ROM/Flash boot loader. Maybe to tell it what to do after rebooting. ??? -rtsig-max & rtsig-nr -==================== - -The file rtsig-max can be used to tune the maximum number -of POSIX realtime (queued) signals that can be outstanding -in the system. - -rtsig-nr shows the number of RT signals currently queued. - - sched_energy_aware ================== From dff2c2e69f308c1c7d296d49d2b0467e9675b58e Mon Sep 17 00:00:00 2001 From: Bhaskar Chowdhury Date: Tue, 18 Feb 2020 15:10:13 +0530 Subject: [PATCH 0344/2231] Replace dead urls with active urls for Mutt This patch replace stale/dead urls with active urls for Mutt. Signed-off-by: Bhaskar Chowdhury Signed-off-by: Jonathan Corbet --- Documentation/process/email-clients.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/process/email-clients.rst b/Documentation/process/email-clients.rst index 5273d06c8ff6..c9e4ce2613c0 100644 --- a/Documentation/process/email-clients.rst +++ b/Documentation/process/email-clients.rst @@ -237,9 +237,9 @@ using Mutt to send patches through Gmail:: The Mutt docs have lots more information: - http://dev.mutt.org/trac/wiki/UseCases/Gmail + https://gitlab.com/muttmua/mutt/-/wikis/UseCases/Gmail - http://dev.mutt.org/doc/manual.html + http://www.mutt.org/doc/manual/ Pine (TUI) ********** From fb0e0ffe7fc8e0e91481e67665f1d646bfd071f2 Mon Sep 17 00:00:00 2001 From: Tony Fischetti Date: Sun, 16 Feb 2020 19:08:26 -0500 Subject: [PATCH 0345/2231] Documentation: bring process docs up to date The guide to the kernel dev process documentation, for example, contains references to older kernels and their timelines. In addition, one of the "long term support kernels" listed have since reached EOL, and a new one has been named. This patch brings information/tables up to date. Additionally, some very trivial grammatical errors, unclear sentences, and potentially unsavory diction have been edited. Signed-off-by: Tony Fischetti Reviewed-by: Randy Dunlap Signed-off-by: Jonathan Corbet --- Documentation/process/2.Process.rst | 108 +++++++++++++------------ Documentation/process/coding-style.rst | 18 ++--- Documentation/process/howto.rst | 17 ++-- 3 files changed, 73 insertions(+), 70 deletions(-) diff --git a/Documentation/process/2.Process.rst b/Documentation/process/2.Process.rst index ae020d84d7c4..b21b5b245d13 100644 --- a/Documentation/process/2.Process.rst +++ b/Documentation/process/2.Process.rst @@ -18,18 +18,18 @@ major kernel release happening every two or three months. The recent release history looks like this: ====== ================= - 4.11 April 30, 2017 - 4.12 July 2, 2017 - 4.13 September 3, 2017 - 4.14 November 12, 2017 - 4.15 January 28, 2018 - 4.16 April 1, 2018 + 5.0 March 3, 2019 + 5.1 May 5, 2019 + 5.2 July 7, 2019 + 5.3 September 15, 2019 + 5.4 November 24, 2019 + 5.5 January 6, 2020 ====== ================= -Every 4.x release is a major kernel release with new features, internal -API changes, and more. A typical 4.x release contain about 13,000 -changesets with changes to several hundred thousand lines of code. 4.x is -thus the leading edge of Linux kernel development; the kernel uses a +Every 5.x release is a major kernel release with new features, internal +API changes, and more. A typical release can contain about 13,000 +changesets with changes to several hundred thousand lines of code. 5.x is +the leading edge of Linux kernel development; the kernel uses a rolling development model which is continually integrating major changes. A relatively straightforward discipline is followed with regard to the @@ -48,9 +48,9 @@ detail later on). The merge window lasts for approximately two weeks. At the end of this time, Linus Torvalds will declare that the window is closed and release the -first of the "rc" kernels. For the kernel which is destined to be 2.6.40, +first of the "rc" kernels. For the kernel which is destined to be 5.6, for example, the release which happens at the end of the merge window will -be called 2.6.40-rc1. The -rc1 release is the signal that the time to +be called 5.6-rc1. The -rc1 release is the signal that the time to merge new features has passed, and that the time to stabilize the next kernel has begun. @@ -67,22 +67,23 @@ add at any time). As fixes make their way into the mainline, the patch rate will slow over time. Linus releases new -rc kernels about once a week; a normal series will get up to somewhere between -rc6 and -rc9 before the kernel is -considered to be sufficiently stable and the final 2.6.x release is made. +considered to be sufficiently stable and the final release is made. At that point the whole process starts over again. -As an example, here is how the 4.16 development cycle went (all dates in -2018): +As an example, here is how the 5.4 development cycle went (all dates in +2019): ============== =============================== - January 28 4.15 stable release - February 11 4.16-rc1, merge window closes - February 18 4.16-rc2 - February 25 4.16-rc3 - March 4 4.16-rc4 - March 11 4.16-rc5 - March 18 4.16-rc6 - March 25 4.16-rc7 - April 1 4.16 stable release + September 15 5.3 stable release + September 30 5.4-rc1, merge window closes + October 6 5.4-rc2 + October 13 5.4-rc3 + October 20 5.4-rc4 + October 27 5.4-rc5 + November 3 5.4-rc6 + November 10 5.4-rc7 + November 17 5.4-rc8 + November 24 5.4 stable release ============== =============================== How do the developers decide when to close the development cycle and create @@ -98,43 +99,44 @@ release is made. In the real world, this kind of perfection is hard to achieve; there are just too many variables in a project of this size. There comes a point where delaying the final release just makes the problem worse; the pile of changes waiting for the next merge window will grow -larger, creating even more regressions the next time around. So most 4.x +larger, creating even more regressions the next time around. So most 5.x kernels go out with a handful of known regressions though, hopefully, none of them are serious. Once a stable release is made, its ongoing maintenance is passed off to the -"stable team," currently consisting of Greg Kroah-Hartman. The stable team -will release occasional updates to the stable release using the 4.x.y -numbering scheme. To be considered for an update release, a patch must (1) -fix a significant bug, and (2) already be merged into the mainline for the -next development kernel. Kernels will typically receive stable updates for -a little more than one development cycle past their initial release. So, -for example, the 4.13 kernel's history looked like: +"stable team," currently Greg Kroah-Hartman. The stable team will release +occasional updates to the stable release using the 5.x.y numbering scheme. +To be considered for an update release, a patch must (1) fix a significant +bug, and (2) already be merged into the mainline for the next development +kernel. Kernels will typically receive stable updates for a little more +than one development cycle past their initial release. So, for example, the +5.2 kernel's history looked like this (all dates in 2019): ============== =============================== - September 3 4.13 stable release - September 13 4.13.1 - September 20 4.13.2 - September 27 4.13.3 - October 5 4.13.4 - October 12 4.13.5 + September 15 5.2 stable release + July 14 5.2.1 + July 21 5.2.2 + July 26 5.2.3 + July 28 5.2.4 + July 31 5.2.5 ... ... - November 24 4.13.16 + October 11 5.2.21 ============== =============================== -4.13.16 was the final stable update of the 4.13 release. +5.2.21 was the final stable update of the 5.2 release. Some kernels are designated "long term" kernels; they will receive support for a longer period. As of this writing, the current long term kernels and their maintainers are: - ====== ====================== ============================== - 3.16 Ben Hutchings (very long-term stable kernel) - 4.1 Sasha Levin - 4.4 Greg Kroah-Hartman (very long-term stable kernel) - 4.9 Greg Kroah-Hartman - 4.14 Greg Kroah-Hartman - ====== ====================== ============================== + ====== ================================ ======================= + 3.16 Ben Hutchings (very long-term kernel) + 4.4 Greg Kroah-Hartman & Sasha Levin (very long-term kernel) + 4.9 Greg Kroah-Hartman & Sasha Levin + 4.14 Greg Kroah-Hartman & Sasha Levin + 4.19 Greg Kroah-Hartman & Sasha Levin + 5.4 Greg Kroah-Hartman & Sasha Levin + ====== ================================ ======================= The selection of a kernel for long-term support is purely a matter of a maintainer having the need and the time to maintain that release. There @@ -215,12 +217,12 @@ How patches get into the Kernel ------------------------------- There is exactly one person who can merge patches into the mainline kernel -repository: Linus Torvalds. But, of the over 9,500 patches which went -into the 2.6.38 kernel, only 112 (around 1.3%) were directly chosen by Linus -himself. The kernel project has long since grown to a size where no single -developer could possibly inspect and select every patch unassisted. The -way the kernel developers have addressed this growth is through the use of -a lieutenant system built around a chain of trust. +repository: Linus Torvalds. But, for example, of the over 9,500 patches +which went into the 2.6.38 kernel, only 112 (around 1.3%) were directly +chosen by Linus himself. The kernel project has long since grown to a size +where no single developer could possibly inspect and select every patch +unassisted. The way the kernel developers have addressed this growth is +through the use of a lieutenant system built around a chain of trust. The kernel code base is logically broken down into a set of subsystems: networking, specific architecture support, memory management, video diff --git a/Documentation/process/coding-style.rst b/Documentation/process/coding-style.rst index edb296c52f61..acb2f1b36350 100644 --- a/Documentation/process/coding-style.rst +++ b/Documentation/process/coding-style.rst @@ -284,9 +284,9 @@ context lines. 4) Naming --------- -C is a Spartan language, and so should your naming be. Unlike Modula-2 -and Pascal programmers, C programmers do not use cute names like -ThisVariableIsATemporaryCounter. A C programmer would call that +C is a Spartan language, and your naming conventions should follow suit. +Unlike Modula-2 and Pascal programmers, C programmers do not use cute +names like ThisVariableIsATemporaryCounter. A C programmer would call that variable ``tmp``, which is much easier to write, and not the least more difficult to understand. @@ -300,9 +300,9 @@ that counts the number of active users, you should call that ``count_active_users()`` or similar, you should **not** call it ``cntusr()``. Encoding the type of a function into the name (so-called Hungarian -notation) is brain damaged - the compiler knows the types anyway and can -check those, and it only confuses the programmer. No wonder MicroSoft -makes buggy programs. +notation) is asinine - the compiler knows the types anyway and can check +those, and it only confuses the programmer. No wonder Microsoft makes buggy +programs. LOCAL variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called ``i``. @@ -806,9 +806,9 @@ covers RTL which is used frequently with assembly language in the kernel. ---------------------------- Kernel developers like to be seen as literate. Do mind the spelling -of kernel messages to make a good impression. Do not use crippled -words like ``dont``; use ``do not`` or ``don't`` instead. Make the messages -concise, clear, and unambiguous. +of kernel messages to make a good impression. Do not use incorrect +contractions like ``dont``; use ``do not`` or ``don't`` instead. Make the +messages concise, clear, and unambiguous. Kernel messages do not have to be terminated with a period. diff --git a/Documentation/process/howto.rst b/Documentation/process/howto.rst index b6f5a379ad6c..70791e153de1 100644 --- a/Documentation/process/howto.rst +++ b/Documentation/process/howto.rst @@ -243,10 +243,10 @@ branches. These different branches are: Mainline tree ~~~~~~~~~~~~~ -Mainline tree are maintained by Linus Torvalds, and can be found at +The mainline tree is maintained by Linus Torvalds, and can be found at https://kernel.org or in the repo. Its development process is as follows: - - As soon as a new kernel is released a two weeks window is open, + - As soon as a new kernel is released a two week window is open, during this period of time maintainers can submit big diffs to Linus, usually the patches that have already been included in the linux-next for a few weeks. The preferred way to submit big changes @@ -281,8 +281,9 @@ Various stable trees with multiple major numbers Kernels with 3-part versions are -stable kernels. They contain relatively small and critical fixes for security problems or significant -regressions discovered in a given major mainline release, with the first -2-part of version number are the same correspondingly. +regressions discovered in a given major mainline release. Each release +in a major stable series increments the third part of the version +number, keeping the first two parts the same. This is the recommended branch for users who want the most recent stable kernel and are not interested in helping test development/experimental @@ -359,10 +360,10 @@ Managing bug reports One of the best ways to put into practice your hacking skills is by fixing bugs reported by other people. Not only you will help to make the kernel -more stable, you'll learn to fix real world problems and you will improve -your skills, and other developers will be aware of your presence. Fixing -bugs is one of the best ways to get merits among other developers, because -not many people like wasting time fixing other people's bugs. +more stable, but you'll also learn to fix real world problems and you will +improve your skills, and other developers will be aware of your presence. +Fixing bugs is one of the best ways to get merits among other developers, +because not many people like wasting time fixing other people's bugs. To work in the already reported bug reports, go to https://bugzilla.kernel.org. From 965fc39f73932041441e03730db31516e285b61a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 15 Feb 2020 23:26:06 -0800 Subject: [PATCH 0346/2231] Documentation: sort _SPHINXDIRS for 'make help' Sort the _SPHINXDIRS so that the 'make help' output is easier to read & search and in a predictable order instead of some unknown pseudo-random order. Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Signed-off-by: Jonathan Corbet --- Documentation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index d77bb607aea4..79ecee62d597 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -13,7 +13,7 @@ endif SPHINXBUILD = sphinx-build SPHINXOPTS = SPHINXDIRS = . -_SPHINXDIRS = $(patsubst $(srctree)/Documentation/%/index.rst,%,$(wildcard $(srctree)/Documentation/*/index.rst)) +_SPHINXDIRS = $(sort $(patsubst $(srctree)/Documentation/%/index.rst,%,$(wildcard $(srctree)/Documentation/*/index.rst))) SPHINX_CONF = conf.py PAPER = BUILDDIR = $(obj)/output From 1733ec77d34059cd67a7b9677fe2fd3ef977afb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Fri, 14 Feb 2020 18:41:32 +0100 Subject: [PATCH 0347/2231] docs: driver-api: edid: Fix list formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without the empty lines, Sphinx renders the list as part of the running text. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jonathan Corbet --- Documentation/driver-api/edid.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/driver-api/edid.rst b/Documentation/driver-api/edid.rst index b1b5acd501ed..7dc07942ceb2 100644 --- a/Documentation/driver-api/edid.rst +++ b/Documentation/driver-api/edid.rst @@ -11,11 +11,13 @@ Today, with the advent of Kernel Mode Setting, a graphics board is either correctly working because all components follow the standards - or the computer is unusable, because the screen remains dark after booting or it displays the wrong area. Cases when this happens are: + - The graphics board does not recognize the monitor. - The graphics board is unable to detect any EDID data. - The graphics board incorrectly forwards EDID data to the driver. - The monitor sends no or bogus EDID data. - A KVM sends its own EDID data instead of querying the connected monitor. + Adding the kernel parameter "nomodeset" helps in most cases, but causes restrictions later on. From 320bfd91a985f2b945bad611c43add8a3a359845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Fri, 14 Feb 2020 18:41:33 +0100 Subject: [PATCH 0348/2231] docs: admin-guide: Move edid.rst from driver-api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This document describes actions that an admin can do, rather than interfaces available to driver developers, so admin-guide seems to be a more appropriate place for it. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jonathan Corbet --- Documentation/{driver-api => admin-guide}/edid.rst | 0 Documentation/admin-guide/index.rst | 1 + Documentation/driver-api/index.rst | 1 - 3 files changed, 1 insertion(+), 1 deletion(-) rename Documentation/{driver-api => admin-guide}/edid.rst (100%) diff --git a/Documentation/driver-api/edid.rst b/Documentation/admin-guide/edid.rst similarity index 100% rename from Documentation/driver-api/edid.rst rename to Documentation/admin-guide/edid.rst diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst index f1d0ccffbe72..5a6269fb8593 100644 --- a/Documentation/admin-guide/index.rst +++ b/Documentation/admin-guide/index.rst @@ -75,6 +75,7 @@ configure specific aspects of kernel behavior to your liking. cputopology dell_rbu device-mapper/index + edid efi-stub ext4 nfs/index diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 0ebe205efd0c..ea3003b3c5e5 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -74,7 +74,6 @@ available subsections can be seen below. connector console dcdbas - edid eisa ipmb isa From b4ce545f349b711351ec4b0df7a3302d91c3dd45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Fri, 14 Feb 2020 18:41:35 +0100 Subject: [PATCH 0349/2231] docs: admin-guide: edid: Clarify where to run "make" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When both the documentation and the data files lived in Documentation/EDID, this wasn't necessary, but both have been moved to other directories in the meantime. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/edid.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/edid.rst b/Documentation/admin-guide/edid.rst index 7dc07942ceb2..80deeb21a265 100644 --- a/Documentation/admin-guide/edid.rst +++ b/Documentation/admin-guide/edid.rst @@ -34,7 +34,7 @@ individual data for a specific misbehaving monitor, commented sources and a Makefile environment are given here. To create binary EDID and C source code files from the existing data -material, simply type "make". +material, simply type "make" in tools/edid/. If you want to create your own EDID file, copy the file 1024x768.S, replace the settings with your own data and add a new target to the From e2c79ab7d75b4c6ed827e8078e5ebe2d059edafc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Fri, 14 Feb 2020 18:41:34 +0100 Subject: [PATCH 0350/2231] tools/edid: Move EDID data sets from Documentation/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The EDID files are not really documentation. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Jonathan Corbet --- {Documentation/EDID => tools/edid}/1024x768.S | 0 {Documentation/EDID => tools/edid}/1280x1024.S | 0 {Documentation/EDID => tools/edid}/1600x1200.S | 0 {Documentation/EDID => tools/edid}/1680x1050.S | 0 {Documentation/EDID => tools/edid}/1920x1080.S | 0 {Documentation/EDID => tools/edid}/800x600.S | 0 {Documentation/EDID => tools/edid}/Makefile | 0 {Documentation/EDID => tools/edid}/edid.S | 0 {Documentation/EDID => tools/edid}/hex | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {Documentation/EDID => tools/edid}/1024x768.S (100%) rename {Documentation/EDID => tools/edid}/1280x1024.S (100%) rename {Documentation/EDID => tools/edid}/1600x1200.S (100%) rename {Documentation/EDID => tools/edid}/1680x1050.S (100%) rename {Documentation/EDID => tools/edid}/1920x1080.S (100%) rename {Documentation/EDID => tools/edid}/800x600.S (100%) rename {Documentation/EDID => tools/edid}/Makefile (100%) rename {Documentation/EDID => tools/edid}/edid.S (100%) rename {Documentation/EDID => tools/edid}/hex (100%) diff --git a/Documentation/EDID/1024x768.S b/tools/edid/1024x768.S similarity index 100% rename from Documentation/EDID/1024x768.S rename to tools/edid/1024x768.S diff --git a/Documentation/EDID/1280x1024.S b/tools/edid/1280x1024.S similarity index 100% rename from Documentation/EDID/1280x1024.S rename to tools/edid/1280x1024.S diff --git a/Documentation/EDID/1600x1200.S b/tools/edid/1600x1200.S similarity index 100% rename from Documentation/EDID/1600x1200.S rename to tools/edid/1600x1200.S diff --git a/Documentation/EDID/1680x1050.S b/tools/edid/1680x1050.S similarity index 100% rename from Documentation/EDID/1680x1050.S rename to tools/edid/1680x1050.S diff --git a/Documentation/EDID/1920x1080.S b/tools/edid/1920x1080.S similarity index 100% rename from Documentation/EDID/1920x1080.S rename to tools/edid/1920x1080.S diff --git a/Documentation/EDID/800x600.S b/tools/edid/800x600.S similarity index 100% rename from Documentation/EDID/800x600.S rename to tools/edid/800x600.S diff --git a/Documentation/EDID/Makefile b/tools/edid/Makefile similarity index 100% rename from Documentation/EDID/Makefile rename to tools/edid/Makefile diff --git a/Documentation/EDID/edid.S b/tools/edid/edid.S similarity index 100% rename from Documentation/EDID/edid.S rename to tools/edid/edid.S diff --git a/Documentation/EDID/hex b/tools/edid/hex similarity index 100% rename from Documentation/EDID/hex rename to tools/edid/hex From 2976908e4198aa02fc3f76802358f69396267189 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Wed, 19 Feb 2020 08:16:11 -0500 Subject: [PATCH 0351/2231] x86/mce: Do not log spurious corrected mce errors A user has reported that they are seeing spurious corrected errors on their hardware. Intel Errata HSD131, HSM142, HSW131, and BDM48 report that "spurious corrected errors may be logged in the IA32_MC0_STATUS register with the valid field (bit 63) set, the uncorrected error field (bit 61) not set, a Model Specific Error Code (bits [31:16]) of 0x000F, and an MCA Error Code (bits [15:0]) of 0x0005." The Errata PDFs are linked in the bugzilla below. Block these spurious errors from the console and logs. [ bp: Move the intel_filter_mce() header declarations into the already existing CONFIG_X86_MCE_INTEL ifdeffery. ] Co-developed-by: Alexander Krupp Signed-off-by: Alexander Krupp Signed-off-by: Prarit Bhargava Signed-off-by: Borislav Petkov Link: https://bugzilla.kernel.org/show_bug.cgi?id=206587 Link: https://lkml.kernel.org/r/20200219131611.36816-1-prarit@redhat.com --- arch/x86/kernel/cpu/mce/core.c | 2 ++ arch/x86/kernel/cpu/mce/intel.c | 17 +++++++++++++++++ arch/x86/kernel/cpu/mce/internal.h | 2 ++ 3 files changed, 21 insertions(+) diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 2c4f949611e4..fe3983d551cc 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -1877,6 +1877,8 @@ bool filter_mce(struct mce *m) { if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) return amd_filter_mce(m); + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) + return intel_filter_mce(m); return false; } diff --git a/arch/x86/kernel/cpu/mce/intel.c b/arch/x86/kernel/cpu/mce/intel.c index 5627b1091b85..989148e6746c 100644 --- a/arch/x86/kernel/cpu/mce/intel.c +++ b/arch/x86/kernel/cpu/mce/intel.c @@ -520,3 +520,20 @@ void mce_intel_feature_clear(struct cpuinfo_x86 *c) { intel_clear_lmce(); } + +bool intel_filter_mce(struct mce *m) +{ + struct cpuinfo_x86 *c = &boot_cpu_data; + + /* MCE errata HSD131, HSM142, HSW131, BDM48, and HSM142 */ + if ((c->x86 == 6) && + ((c->x86_model == INTEL_FAM6_HASWELL) || + (c->x86_model == INTEL_FAM6_HASWELL_L) || + (c->x86_model == INTEL_FAM6_BROADWELL) || + (c->x86_model == INTEL_FAM6_HASWELL_G)) && + (m->bank == 0) && + ((m->status & 0xa0000000ffffffff) == 0x80000000000f0005)) + return true; + + return false; +} diff --git a/arch/x86/kernel/cpu/mce/internal.h b/arch/x86/kernel/cpu/mce/internal.h index b785c0d0b590..97db18441d2c 100644 --- a/arch/x86/kernel/cpu/mce/internal.h +++ b/arch/x86/kernel/cpu/mce/internal.h @@ -48,6 +48,7 @@ void cmci_disable_bank(int bank); void intel_init_cmci(void); void intel_init_lmce(void); void intel_clear_lmce(void); +bool intel_filter_mce(struct mce *m); #else # define cmci_intel_adjust_timer mce_adjust_timer_default static inline bool mce_intel_cmci_poll(void) { return false; } @@ -56,6 +57,7 @@ static inline void cmci_disable_bank(int bank) { } static inline void intel_init_cmci(void) { } static inline void intel_init_lmce(void) { } static inline void intel_clear_lmce(void) { } +static inline bool intel_filter_mce(struct mce *m) { return false; }; #endif void mce_timer_kick(unsigned long interval); From ad5906bd6e9aa3e156dcac8fc070b153648e8b68 Mon Sep 17 00:00:00 2001 From: Phong LE Date: Wed, 19 Feb 2020 15:09:06 +0100 Subject: [PATCH 0352/2231] regmap: wrong descriptions in regmap_range_cfg Swap selector_mask and selector_shift descriptions Signed-off-by: Phong LE Link: https://lore.kernel.org/r/20200219140906.29180-1-ple@baylibre.com Signed-off-by: Mark Brown --- include/linux/regmap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f0a092a1a96d..40b07168fd8e 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -461,8 +461,8 @@ struct regmap_config { * @range_max: Address of the highest register in virtual range. * * @selector_reg: Register with selector field. - * @selector_mask: Bit shift for selector value. - * @selector_shift: Bit mask for selector value. + * @selector_mask: Bit mask for selector value. + * @selector_shift: Bit shift for selector value. * * @window_start: Address of first (lowest) register in data window. * @window_len: Number of registers in data window. From db6c122b8fb16a346b87fbcb4987515429e5e7cf Mon Sep 17 00:00:00 2001 From: Lei Wang Date: Mon, 27 Jan 2020 08:23:08 -0800 Subject: [PATCH 0353/2231] dt-bindings: edac: Dmc-520.yaml Add the device tree bindings for the EDAC driver dmc520_edac. Signed-off-by: Lei Wang Signed-off-by: Shiping Ji Signed-off-by: Borislav Petkov Reviewed-by: James Morse Reviewed-by: Rob Herring Link: https://lkml.kernel.org/r/5354a9c3-5b5a-486a-9d19-fa9be169faef@gmail.com --- .../devicetree/bindings/edac/dmc-520.yaml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/edac/dmc-520.yaml diff --git a/Documentation/devicetree/bindings/edac/dmc-520.yaml b/Documentation/devicetree/bindings/edac/dmc-520.yaml new file mode 100644 index 000000000000..9272d2bd8634 --- /dev/null +++ b/Documentation/devicetree/bindings/edac/dmc-520.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/edac/dmc-520.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM DMC-520 EDAC bindings + +maintainers: + - Lei Wang + +description: |+ + DMC-520 node is defined to describe DRAM error detection and correction. + + https://static.docs.arm.com/100000/0200/corelink_dmc520_trm_100000_0200_01_en.pdf + +properties: + compatible: + items: + - const: brcm,dmc-520 + - const: arm,dmc-520 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 10 + + interrupt-names: + minItems: 1 + maxItems: 10 + items: + enum: + - ram_ecc_errc + - ram_ecc_errd + - dram_ecc_errc + - dram_ecc_errd + - failed_access + - failed_prog + - link_err + - temperature_event + - arch_fsm + - phy_request + +required: + - compatible + - reg + - interrupts + - interrupt-names + +examples: + - | + dmc0: dmc@200000 { + compatible = "brcm,dmc-520", "arm,dmc-520"; + reg = <0x200000 0x80000>; + interrupts = <0x0 0x349 0x4>, <0x0 0x34B 0x4>; + interrupt-names = "dram_ecc_errc", "dram_ecc_errd"; + }; From 1088750d78392cf79c113082ffbf955384737e9c Mon Sep 17 00:00:00 2001 From: Lei Wang Date: Wed, 22 Jan 2020 16:31:14 -0800 Subject: [PATCH 0354/2231] EDAC: Add EDAC driver for DMC520 The driver supports error detection and correction on devices with an ARM DMC-520 memory controller. Signed-off-by: Lei Wang Signed-off-by: Shiping Ji Signed-off-by: Borislav Petkov Reviewed-by: James Morse Link: https://lkml.kernel.org/r/83b48c70-dc06-d0d4-cae9-a2187fca628b@gmail.com --- MAINTAINERS | 6 + drivers/edac/Kconfig | 7 + drivers/edac/Makefile | 1 + drivers/edac/dmc520_edac.c | 656 +++++++++++++++++++++++++++++++++++++ 4 files changed, 670 insertions(+) create mode 100644 drivers/edac/dmc520_edac.c diff --git a/MAINTAINERS b/MAINTAINERS index a0d86490c2c6..8456c8ecb073 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5998,6 +5998,12 @@ F: Documentation/driver-api/edac.rst F: drivers/edac/ F: include/linux/edac.h +EDAC-DMC520 +M: Lei Wang +L: linux-edac@vger.kernel.org +S: Supported +F: drivers/edac/dmc520_edac.c + EDAC-E752X M: Mark Gross L: linux-edac@vger.kernel.org diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index b3c99bb5fe77..fe2eb892a1bd 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -523,4 +523,11 @@ config EDAC_BLUEFIELD Support for error detection and correction on the Mellanox BlueField SoCs. +config EDAC_DMC520 + tristate "ARM DMC-520 ECC" + depends on ARM64 + help + Support for error detection and correction on the + SoCs with ARM DMC-520 DRAM controller. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index d77200c9680b..269e15118cea 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -87,3 +87,4 @@ obj-$(CONFIG_EDAC_TI) += ti_edac.o obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o +obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o diff --git a/drivers/edac/dmc520_edac.c b/drivers/edac/dmc520_edac.c new file mode 100644 index 000000000000..fc1153ab1ebb --- /dev/null +++ b/drivers/edac/dmc520_edac.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * EDAC driver for DMC-520 memory controller. + * + * The driver supports 10 interrupt lines, + * though only dram_ecc_errc and dram_ecc_errd are currently handled. + * + * Authors: Rui Zhao + * Lei Wang + * Shiping Ji + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "edac_mc.h" + +/* DMC-520 registers */ +#define REG_OFFSET_FEATURE_CONFIG 0x130 +#define REG_OFFSET_ECC_ERRC_COUNT_31_00 0x158 +#define REG_OFFSET_ECC_ERRC_COUNT_63_32 0x15C +#define REG_OFFSET_ECC_ERRD_COUNT_31_00 0x160 +#define REG_OFFSET_ECC_ERRD_COUNT_63_32 0x164 +#define REG_OFFSET_INTERRUPT_CONTROL 0x500 +#define REG_OFFSET_INTERRUPT_CLR 0x508 +#define REG_OFFSET_INTERRUPT_STATUS 0x510 +#define REG_OFFSET_DRAM_ECC_ERRC_INT_INFO_31_00 0x528 +#define REG_OFFSET_DRAM_ECC_ERRC_INT_INFO_63_32 0x52C +#define REG_OFFSET_DRAM_ECC_ERRD_INT_INFO_31_00 0x530 +#define REG_OFFSET_DRAM_ECC_ERRD_INT_INFO_63_32 0x534 +#define REG_OFFSET_ADDRESS_CONTROL_NOW 0x1010 +#define REG_OFFSET_MEMORY_TYPE_NOW 0x1128 +#define REG_OFFSET_SCRUB_CONTROL0_NOW 0x1170 +#define REG_OFFSET_FORMAT_CONTROL 0x18 + +/* DMC-520 types, masks and bitfields */ +#define RAM_ECC_INT_CE_BIT BIT(0) +#define RAM_ECC_INT_UE_BIT BIT(1) +#define DRAM_ECC_INT_CE_BIT BIT(2) +#define DRAM_ECC_INT_UE_BIT BIT(3) +#define FAILED_ACCESS_INT_BIT BIT(4) +#define FAILED_PROG_INT_BIT BIT(5) +#define LINK_ERR_INT_BIT BIT(6) +#define TEMPERATURE_EVENT_INT_BIT BIT(7) +#define ARCH_FSM_INT_BIT BIT(8) +#define PHY_REQUEST_INT_BIT BIT(9) +#define MEMORY_WIDTH_MASK GENMASK(1, 0) +#define SCRUB_TRIGGER0_NEXT_MASK GENMASK(1, 0) +#define REG_FIELD_DRAM_ECC_ENABLED GENMASK(1, 0) +#define REG_FIELD_MEMORY_TYPE GENMASK(2, 0) +#define REG_FIELD_DEVICE_WIDTH GENMASK(9, 8) +#define REG_FIELD_ADDRESS_CONTROL_COL GENMASK(2, 0) +#define REG_FIELD_ADDRESS_CONTROL_ROW GENMASK(10, 8) +#define REG_FIELD_ADDRESS_CONTROL_BANK GENMASK(18, 16) +#define REG_FIELD_ADDRESS_CONTROL_RANK GENMASK(25, 24) +#define REG_FIELD_ERR_INFO_LOW_VALID BIT(0) +#define REG_FIELD_ERR_INFO_LOW_COL GENMASK(10, 1) +#define REG_FIELD_ERR_INFO_LOW_ROW GENMASK(28, 11) +#define REG_FIELD_ERR_INFO_LOW_RANK GENMASK(31, 29) +#define REG_FIELD_ERR_INFO_HIGH_BANK GENMASK(3, 0) +#define REG_FIELD_ERR_INFO_HIGH_VALID BIT(31) + +#define DRAM_ADDRESS_CONTROL_MIN_COL_BITS 8 +#define DRAM_ADDRESS_CONTROL_MIN_ROW_BITS 11 + +#define DMC520_SCRUB_TRIGGER_ERR_DETECT 2 +#define DMC520_SCRUB_TRIGGER_IDLE 3 + +/* Driver settings */ +/* + * The max-length message would be: "rank:7 bank:15 row:262143 col:1023". + * Max length is 34. Using a 40-size buffer is enough. + */ +#define DMC520_MSG_BUF_SIZE 40 +#define EDAC_MOD_NAME "dmc520-edac" +#define EDAC_CTL_NAME "dmc520" + +/* the data bus width for the attached memory chips. */ +enum dmc520_mem_width { + MEM_WIDTH_X32 = 2, + MEM_WIDTH_X64 = 3 +}; + +/* memory type */ +enum dmc520_mem_type { + MEM_TYPE_DDR3 = 1, + MEM_TYPE_DDR4 = 2 +}; + +/* memory device width */ +enum dmc520_dev_width { + DEV_WIDTH_X4 = 0, + DEV_WIDTH_X8 = 1, + DEV_WIDTH_X16 = 2 +}; + +struct ecc_error_info { + u32 col; + u32 row; + u32 bank; + u32 rank; +}; + +/* The interrupt config */ +struct dmc520_irq_config { + char *name; + int mask; +}; + +/* The interrupt mappings */ +static struct dmc520_irq_config dmc520_irq_configs[] = { + { + .name = "ram_ecc_errc", + .mask = RAM_ECC_INT_CE_BIT + }, + { + .name = "ram_ecc_errd", + .mask = RAM_ECC_INT_UE_BIT + }, + { + .name = "dram_ecc_errc", + .mask = DRAM_ECC_INT_CE_BIT + }, + { + .name = "dram_ecc_errd", + .mask = DRAM_ECC_INT_UE_BIT + }, + { + .name = "failed_access", + .mask = FAILED_ACCESS_INT_BIT + }, + { + .name = "failed_prog", + .mask = FAILED_PROG_INT_BIT + }, + { + .name = "link_err", + .mask = LINK_ERR_INT_BIT + }, + { + .name = "temperature_event", + .mask = TEMPERATURE_EVENT_INT_BIT + }, + { + .name = "arch_fsm", + .mask = ARCH_FSM_INT_BIT + }, + { + .name = "phy_request", + .mask = PHY_REQUEST_INT_BIT + } +}; + +#define NUMBER_OF_IRQS ARRAY_SIZE(dmc520_irq_configs) + +/* + * The EDAC driver private data. + * error_lock is to protect concurrent writes to the mci->error_desc through + * edac_mc_handle_error(). + */ +struct dmc520_edac { + void __iomem *reg_base; + spinlock_t error_lock; + u32 mem_width_in_bytes; + int irqs[NUMBER_OF_IRQS]; + int masks[NUMBER_OF_IRQS]; +}; + +static int dmc520_mc_idx; + +static u32 dmc520_read_reg(struct dmc520_edac *pvt, u32 offset) +{ + return readl(pvt->reg_base + offset); +} + +static void dmc520_write_reg(struct dmc520_edac *pvt, u32 val, u32 offset) +{ + writel(val, pvt->reg_base + offset); +} + +static u32 dmc520_calc_dram_ecc_error(u32 value) +{ + u32 total = 0; + + /* Each rank's error counter takes one byte. */ + while (value > 0) { + total += (value & 0xFF); + value >>= 8; + } + return total; +} + +static u32 dmc520_get_dram_ecc_error_count(struct dmc520_edac *pvt, + bool is_ce) +{ + u32 reg_offset_low, reg_offset_high; + u32 err_low, err_high; + u32 err_count; + + reg_offset_low = is_ce ? REG_OFFSET_ECC_ERRC_COUNT_31_00 : + REG_OFFSET_ECC_ERRD_COUNT_31_00; + reg_offset_high = is_ce ? REG_OFFSET_ECC_ERRC_COUNT_63_32 : + REG_OFFSET_ECC_ERRD_COUNT_63_32; + + err_low = dmc520_read_reg(pvt, reg_offset_low); + err_high = dmc520_read_reg(pvt, reg_offset_high); + /* Reset error counters */ + dmc520_write_reg(pvt, 0, reg_offset_low); + dmc520_write_reg(pvt, 0, reg_offset_high); + + err_count = dmc520_calc_dram_ecc_error(err_low) + + dmc520_calc_dram_ecc_error(err_high); + + return err_count; +} + +static void dmc520_get_dram_ecc_error_info(struct dmc520_edac *pvt, + bool is_ce, + struct ecc_error_info *info) +{ + u32 reg_offset_low, reg_offset_high; + u32 reg_val_low, reg_val_high; + bool valid; + + reg_offset_low = is_ce ? REG_OFFSET_DRAM_ECC_ERRC_INT_INFO_31_00 : + REG_OFFSET_DRAM_ECC_ERRD_INT_INFO_31_00; + reg_offset_high = is_ce ? REG_OFFSET_DRAM_ECC_ERRC_INT_INFO_63_32 : + REG_OFFSET_DRAM_ECC_ERRD_INT_INFO_63_32; + + reg_val_low = dmc520_read_reg(pvt, reg_offset_low); + reg_val_high = dmc520_read_reg(pvt, reg_offset_high); + + valid = (FIELD_GET(REG_FIELD_ERR_INFO_LOW_VALID, reg_val_low) != 0) && + (FIELD_GET(REG_FIELD_ERR_INFO_HIGH_VALID, reg_val_high) != 0); + + if (valid) { + info->col = FIELD_GET(REG_FIELD_ERR_INFO_LOW_COL, reg_val_low); + info->row = FIELD_GET(REG_FIELD_ERR_INFO_LOW_ROW, reg_val_low); + info->rank = FIELD_GET(REG_FIELD_ERR_INFO_LOW_RANK, reg_val_low); + info->bank = FIELD_GET(REG_FIELD_ERR_INFO_HIGH_BANK, reg_val_high); + } else { + memset(info, 0, sizeof(*info)); + } +} + +static bool dmc520_is_ecc_enabled(void __iomem *reg_base) +{ + u32 reg_val = readl(reg_base + REG_OFFSET_FEATURE_CONFIG); + + return FIELD_GET(REG_FIELD_DRAM_ECC_ENABLED, reg_val); +} + +static enum scrub_type dmc520_get_scrub_type(struct dmc520_edac *pvt) +{ + enum scrub_type type = SCRUB_NONE; + u32 reg_val, scrub_cfg; + + reg_val = dmc520_read_reg(pvt, REG_OFFSET_SCRUB_CONTROL0_NOW); + scrub_cfg = FIELD_GET(SCRUB_TRIGGER0_NEXT_MASK, reg_val); + + if (scrub_cfg == DMC520_SCRUB_TRIGGER_ERR_DETECT || + scrub_cfg == DMC520_SCRUB_TRIGGER_IDLE) + type = SCRUB_HW_PROG; + + return type; +} + +/* Get the memory data bus width, in number of bytes. */ +static u32 dmc520_get_memory_width(struct dmc520_edac *pvt) +{ + enum dmc520_mem_width mem_width_field; + u32 mem_width_in_bytes = 0; + u32 reg_val; + + reg_val = dmc520_read_reg(pvt, REG_OFFSET_FORMAT_CONTROL); + mem_width_field = FIELD_GET(MEMORY_WIDTH_MASK, reg_val); + + if (mem_width_field == MEM_WIDTH_X32) + mem_width_in_bytes = 4; + else if (mem_width_field == MEM_WIDTH_X64) + mem_width_in_bytes = 8; + return mem_width_in_bytes; +} + +static enum mem_type dmc520_get_mtype(struct dmc520_edac *pvt) +{ + enum mem_type mt = MEM_UNKNOWN; + enum dmc520_mem_type type; + u32 reg_val; + + reg_val = dmc520_read_reg(pvt, REG_OFFSET_MEMORY_TYPE_NOW); + type = FIELD_GET(REG_FIELD_MEMORY_TYPE, reg_val); + + switch (type) { + case MEM_TYPE_DDR3: + mt = MEM_DDR3; + break; + + case MEM_TYPE_DDR4: + mt = MEM_DDR4; + break; + } + + return mt; +} + +static enum dev_type dmc520_get_dtype(struct dmc520_edac *pvt) +{ + enum dmc520_dev_width device_width; + enum dev_type dt = DEV_UNKNOWN; + u32 reg_val; + + reg_val = dmc520_read_reg(pvt, REG_OFFSET_MEMORY_TYPE_NOW); + device_width = FIELD_GET(REG_FIELD_DEVICE_WIDTH, reg_val); + + switch (device_width) { + case DEV_WIDTH_X4: + dt = DEV_X4; + break; + + case DEV_WIDTH_X8: + dt = DEV_X8; + break; + + case DEV_WIDTH_X16: + dt = DEV_X16; + break; + } + + return dt; +} + +static u32 dmc520_get_rank_count(void __iomem *reg_base) +{ + u32 reg_val, rank_bits; + + reg_val = readl(reg_base + REG_OFFSET_ADDRESS_CONTROL_NOW); + rank_bits = FIELD_GET(REG_FIELD_ADDRESS_CONTROL_RANK, reg_val); + + return BIT(rank_bits); +} + +static u64 dmc520_get_rank_size(struct dmc520_edac *pvt) +{ + u32 reg_val, col_bits, row_bits, bank_bits; + + reg_val = dmc520_read_reg(pvt, REG_OFFSET_ADDRESS_CONTROL_NOW); + + col_bits = FIELD_GET(REG_FIELD_ADDRESS_CONTROL_COL, reg_val) + + DRAM_ADDRESS_CONTROL_MIN_COL_BITS; + row_bits = FIELD_GET(REG_FIELD_ADDRESS_CONTROL_ROW, reg_val) + + DRAM_ADDRESS_CONTROL_MIN_ROW_BITS; + bank_bits = FIELD_GET(REG_FIELD_ADDRESS_CONTROL_BANK, reg_val); + + return (u64)pvt->mem_width_in_bytes << (col_bits + row_bits + bank_bits); +} + +static void dmc520_handle_dram_ecc_errors(struct mem_ctl_info *mci, + bool is_ce) +{ + struct dmc520_edac *pvt = mci->pvt_info; + char message[DMC520_MSG_BUF_SIZE]; + struct ecc_error_info info; + u32 cnt; + + dmc520_get_dram_ecc_error_info(pvt, is_ce, &info); + + cnt = dmc520_get_dram_ecc_error_count(pvt, is_ce); + if (!cnt) + return; + + snprintf(message, ARRAY_SIZE(message), + "rank:%d bank:%d row:%d col:%d", + info.rank, info.bank, + info.row, info.col); + + spin_lock(&pvt->error_lock); + edac_mc_handle_error((is_ce ? HW_EVENT_ERR_CORRECTED : + HW_EVENT_ERR_UNCORRECTED), + mci, cnt, 0, 0, 0, info.rank, -1, -1, + message, ""); + spin_unlock(&pvt->error_lock); +} + +static irqreturn_t dmc520_edac_dram_ecc_isr(int irq, struct mem_ctl_info *mci, + bool is_ce) +{ + struct dmc520_edac *pvt = mci->pvt_info; + u32 i_mask; + + i_mask = is_ce ? DRAM_ECC_INT_CE_BIT : DRAM_ECC_INT_UE_BIT; + + dmc520_handle_dram_ecc_errors(mci, is_ce); + + dmc520_write_reg(pvt, i_mask, REG_OFFSET_INTERRUPT_CLR); + + return IRQ_HANDLED; +} + +static irqreturn_t dmc520_edac_dram_all_isr(int irq, struct mem_ctl_info *mci, + u32 irq_mask) +{ + struct dmc520_edac *pvt = mci->pvt_info; + irqreturn_t irq_ret = IRQ_NONE; + u32 status; + + status = dmc520_read_reg(pvt, REG_OFFSET_INTERRUPT_STATUS); + + if ((irq_mask & DRAM_ECC_INT_CE_BIT) && + (status & DRAM_ECC_INT_CE_BIT)) + irq_ret = dmc520_edac_dram_ecc_isr(irq, mci, true); + + if ((irq_mask & DRAM_ECC_INT_UE_BIT) && + (status & DRAM_ECC_INT_UE_BIT)) + irq_ret = dmc520_edac_dram_ecc_isr(irq, mci, false); + + return irq_ret; +} + +static irqreturn_t dmc520_isr(int irq, void *data) +{ + struct mem_ctl_info *mci = data; + struct dmc520_edac *pvt = mci->pvt_info; + u32 mask = 0; + int idx; + + for (idx = 0; idx < NUMBER_OF_IRQS; idx++) { + if (pvt->irqs[idx] == irq) { + mask = pvt->masks[idx]; + break; + } + } + return dmc520_edac_dram_all_isr(irq, mci, mask); +} + +static void dmc520_init_csrow(struct mem_ctl_info *mci) +{ + struct dmc520_edac *pvt = mci->pvt_info; + struct csrow_info *csi; + struct dimm_info *dimm; + u32 pages_per_rank; + enum dev_type dt; + enum mem_type mt; + int row, ch; + u64 rs; + + dt = dmc520_get_dtype(pvt); + mt = dmc520_get_mtype(pvt); + rs = dmc520_get_rank_size(pvt); + pages_per_rank = rs >> PAGE_SHIFT; + + for (row = 0; row < mci->nr_csrows; row++) { + csi = mci->csrows[row]; + + for (ch = 0; ch < csi->nr_channels; ch++) { + dimm = csi->channels[ch]->dimm; + dimm->grain = pvt->mem_width_in_bytes; + dimm->dtype = dt; + dimm->mtype = mt; + dimm->edac_mode = EDAC_FLAG_SECDED; + dimm->nr_pages = pages_per_rank / csi->nr_channels; + } + } +} + +static int dmc520_edac_probe(struct platform_device *pdev) +{ + bool registered[NUMBER_OF_IRQS] = { false }; + int irqs[NUMBER_OF_IRQS] = { -ENXIO }; + int masks[NUMBER_OF_IRQS] = { 0 }; + struct edac_mc_layer layers[1]; + struct dmc520_edac *pvt = NULL; + struct mem_ctl_info *mci; + void __iomem *reg_base; + u32 irq_mask_all = 0; + struct resource *res; + struct device *dev; + int ret, idx, irq; + u32 reg_val; + + /* Parse the device node */ + dev = &pdev->dev; + + for (idx = 0; idx < NUMBER_OF_IRQS; idx++) { + irq = platform_get_irq_byname(pdev, dmc520_irq_configs[idx].name); + irqs[idx] = irq; + masks[idx] = dmc520_irq_configs[idx].mask; + if (irq >= 0) { + irq_mask_all |= dmc520_irq_configs[idx].mask; + edac_dbg(0, "Discovered %s, irq: %d.\n", dmc520_irq_configs[idx].name, irq); + } + } + + if (!irq_mask_all) { + edac_printk(KERN_ERR, EDAC_MOD_NAME, + "At least one valid interrupt line is expected.\n"); + return -EINVAL; + } + + /* Initialize dmc520 edac */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(reg_base)) + return PTR_ERR(reg_base); + + if (!dmc520_is_ecc_enabled(reg_base)) + return -ENXIO; + + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = dmc520_get_rank_count(reg_base); + layers[0].is_virt_csrow = true; + + mci = edac_mc_alloc(dmc520_mc_idx++, ARRAY_SIZE(layers), layers, sizeof(*pvt)); + if (!mci) { + edac_printk(KERN_ERR, EDAC_MOD_NAME, + "Failed to allocate memory for mc instance\n"); + ret = -ENOMEM; + goto err; + } + + pvt = mci->pvt_info; + + pvt->reg_base = reg_base; + spin_lock_init(&pvt->error_lock); + memcpy(pvt->irqs, irqs, sizeof(irqs)); + memcpy(pvt->masks, masks, sizeof(masks)); + + platform_set_drvdata(pdev, mci); + + mci->pdev = dev; + mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->scrub_cap = SCRUB_FLAG_HW_SRC; + mci->scrub_mode = dmc520_get_scrub_type(pvt); + mci->ctl_name = EDAC_CTL_NAME; + mci->dev_name = dev_name(mci->pdev); + mci->mod_name = EDAC_MOD_NAME; + + edac_op_state = EDAC_OPSTATE_INT; + + pvt->mem_width_in_bytes = dmc520_get_memory_width(pvt); + + dmc520_init_csrow(mci); + + /* Clear interrupts, not affecting other unrelated interrupts */ + reg_val = dmc520_read_reg(pvt, REG_OFFSET_INTERRUPT_CONTROL); + dmc520_write_reg(pvt, reg_val & (~irq_mask_all), + REG_OFFSET_INTERRUPT_CONTROL); + dmc520_write_reg(pvt, irq_mask_all, REG_OFFSET_INTERRUPT_CLR); + + for (idx = 0; idx < NUMBER_OF_IRQS; idx++) { + irq = irqs[idx]; + if (irq >= 0) { + ret = devm_request_irq(&pdev->dev, irq, + dmc520_isr, IRQF_SHARED, + dev_name(&pdev->dev), mci); + if (ret < 0) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed to request irq %d\n", irq); + goto err; + } + registered[idx] = true; + } + } + + /* Reset DRAM CE/UE counters */ + if (irq_mask_all & DRAM_ECC_INT_CE_BIT) + dmc520_get_dram_ecc_error_count(pvt, true); + + if (irq_mask_all & DRAM_ECC_INT_UE_BIT) + dmc520_get_dram_ecc_error_count(pvt, false); + + ret = edac_mc_add_mc(mci); + if (ret) { + edac_printk(KERN_ERR, EDAC_MOD_NAME, + "Failed to register with EDAC core\n"); + goto err; + } + + /* Enable interrupts, not affecting other unrelated interrupts */ + dmc520_write_reg(pvt, reg_val | irq_mask_all, + REG_OFFSET_INTERRUPT_CONTROL); + + return 0; + +err: + for (idx = 0; idx < NUMBER_OF_IRQS; idx++) { + if (registered[idx]) + devm_free_irq(&pdev->dev, pvt->irqs[idx], mci); + } + if (mci) + edac_mc_free(mci); + + return ret; +} + +static int dmc520_edac_remove(struct platform_device *pdev) +{ + u32 reg_val, idx, irq_mask_all = 0; + struct mem_ctl_info *mci; + struct dmc520_edac *pvt; + + mci = platform_get_drvdata(pdev); + pvt = mci->pvt_info; + + /* Disable interrupts */ + reg_val = dmc520_read_reg(pvt, REG_OFFSET_INTERRUPT_CONTROL); + dmc520_write_reg(pvt, reg_val & (~irq_mask_all), + REG_OFFSET_INTERRUPT_CONTROL); + + /* free irq's */ + for (idx = 0; idx < NUMBER_OF_IRQS; idx++) { + if (pvt->irqs[idx] >= 0) { + irq_mask_all |= pvt->masks[idx]; + devm_free_irq(&pdev->dev, pvt->irqs[idx], mci); + } + } + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + + return 0; +} + +static const struct of_device_id dmc520_edac_driver_id[] = { + { .compatible = "arm,dmc-520", }, + { /* end of table */ } +}; + +MODULE_DEVICE_TABLE(of, dmc520_edac_driver_id); + +static struct platform_driver dmc520_edac_driver = { + .driver = { + .name = "dmc520", + .of_match_table = dmc520_edac_driver_id, + }, + + .probe = dmc520_edac_probe, + .remove = dmc520_edac_remove +}; + +module_platform_driver(dmc520_edac_driver); + +MODULE_AUTHOR("Rui Zhao "); +MODULE_AUTHOR("Lei Wang "); +MODULE_AUTHOR("Shiping Ji "); +MODULE_DESCRIPTION("DMC-520 ECC driver"); +MODULE_LICENSE("GPL v2"); From 3c87402771f2bc38ee92eb506bb58e4fc4a5b8b9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 16 Feb 2020 20:54:21 -0800 Subject: [PATCH 0355/2231] Documentation: power: fix pm_qos_interface.rst format warning Fix Sphinx warnings by indenting the bullet list (and making it unnumbered). Documentation/power/pm_qos_interface.rst:12: WARNING: Unexpected indentation. Signed-off-by: Randy Dunlap Signed-off-by: Rafael J. Wysocki --- Documentation/power/pm_qos_interface.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/power/pm_qos_interface.rst b/Documentation/power/pm_qos_interface.rst index 064f668fbdab..69b0fe3e2542 100644 --- a/Documentation/power/pm_qos_interface.rst +++ b/Documentation/power/pm_qos_interface.rst @@ -7,8 +7,8 @@ performance expectations by drivers, subsystems and user space applications on one of the parameters. Two different PM QoS frameworks are available: -1. CPU latency QoS. -2. The per-device PM QoS framework provides the API to manage the + * CPU latency QoS. + * The per-device PM QoS framework provides the API to manage the per-device latency constraints and PM QoS flags. The latency unit used in the PM QoS framework is the microsecond (usec). From 320a4fc2d1b0c2314342dfdd3348270f126196a4 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 28 Jan 2020 13:50:32 +0100 Subject: [PATCH 0356/2231] arm64: Remove TIF_NOHZ The syscall slow path is spuriously invoked when context tracking is activated while the entry code calls context tracking from fast path. Remove that overhead and the unused flag itself while at it. Acked-by: Will Deacon Signed-off-by: Frederic Weisbecker Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Catalin Marinas Cc: Will Deacon --- arch/arm64/Kconfig | 1 - arch/arm64/include/asm/thread_info.h | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 5c945fa3df26..0b30e884e088 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -140,7 +140,6 @@ config ARM64 select HAVE_CMPXCHG_DOUBLE select HAVE_CMPXCHG_LOCAL select HAVE_CONTEXT_TRACKING - select HAVE_TIF_NOHZ select HAVE_COPY_THREAD_TLS select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index f0cec4160136..512174a8e789 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -63,7 +63,6 @@ void arch_release_task_struct(struct task_struct *tsk); #define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */ #define TIF_UPROBE 4 /* uprobe breakpoint or singlestep */ #define TIF_FSCHECK 5 /* Check FS is USER_DS on return */ -#define TIF_NOHZ 7 #define TIF_SYSCALL_TRACE 8 /* syscall trace active */ #define TIF_SYSCALL_AUDIT 9 /* syscall auditing */ #define TIF_SYSCALL_TRACEPOINT 10 /* syscall tracepoint for ftrace */ @@ -83,7 +82,6 @@ void arch_release_task_struct(struct task_struct *tsk); #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_FOREIGN_FPSTATE (1 << TIF_FOREIGN_FPSTATE) -#define _TIF_NOHZ (1 << TIF_NOHZ) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) @@ -100,7 +98,7 @@ void arch_release_task_struct(struct task_struct *tsk); #define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \ - _TIF_NOHZ | _TIF_SYSCALL_EMU) + _TIF_SYSCALL_EMU) #define INIT_THREAD_INFO(tsk) \ { \ From 82e0516ce3a147365a5dd2a9bedd5ba43a18663d Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 3 Feb 2020 19:35:58 -0500 Subject: [PATCH 0357/2231] sched/core: Remove duplicate assignment in sched_tick_remote() A redundant "curr = rq->curr" was added; remove it. Fixes: ebc0f83c78a2 ("timers/nohz: Update NOHZ load in remote tick") Signed-off-by: Scott Wood Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/1580776558-12882-1-git-send-email-swood@redhat.com --- kernel/sched/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 45f79bcc3146..377ec26e9159 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3683,7 +3683,6 @@ static void sched_tick_remote(struct work_struct *work) if (cpu_is_offline(cpu)) goto out_unlock; - curr = rq->curr; update_rq_clock(rq); if (!is_idle_task(curr)) { From b7a331615d254191e7f5f0e35aec9adcd6acdc54 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Thu, 6 Feb 2020 19:19:54 +0000 Subject: [PATCH 0358/2231] sched/fair: Add asymmetric CPU capacity wakeup scan Issue ===== On asymmetric CPU capacity topologies, we currently rely on wake_cap() to drive select_task_rq_fair() towards either: - its slow-path (find_idlest_cpu()) if either the previous or current (waking) CPU has too little capacity for the waking task - its fast-path (select_idle_sibling()) otherwise Commit: 3273163c6775 ("sched/fair: Let asymmetric CPU configurations balance at wake-up") points out that this relies on the assumption that "[...]the CPU capacities within an SD_SHARE_PKG_RESOURCES domain (sd_llc) are homogeneous". This assumption no longer holds on newer generations of big.LITTLE systems (DynamIQ), which can accommodate CPUs of different compute capacity within a single LLC domain. To hopefully paint a better picture, a regular big.LITTLE topology would look like this: +---------+ +---------+ | L2 | | L2 | +----+----+ +----+----+ |CPU0|CPU1| |CPU2|CPU3| +----+----+ +----+----+ ^^^ ^^^ LITTLEs bigs which would result in the following scheduler topology: DIE [ ] <- sd_asym_cpucapacity MC [ ] [ ] <- sd_llc 0 1 2 3 Conversely, a DynamIQ topology could look like: +-------------------+ | L3 | +----+----+----+----+ | L2 | L2 | L2 | L2 | +----+----+----+----+ |CPU0|CPU1|CPU2|CPU3| +----+----+----+----+ ^^^^^ ^^^^^ LITTLEs bigs which would result in the following scheduler topology: MC [ ] <- sd_llc, sd_asym_cpucapacity 0 1 2 3 What this means is that, on DynamIQ systems, we could pass the wake_cap() test (IOW presume the waking task fits on the CPU capacities of some LLC domain), thus go through select_idle_sibling(). This function operates on an LLC domain, which here spans both bigs and LITTLEs, so it could very well pick a CPU of too small capacity for the task, despite there being fitting idle CPUs - it very much depends on the CPU iteration order, on which we have absolutely no guarantees capacity-wise. Implementation ============== Introduce yet another select_idle_sibling() helper function that takes CPU capacity into account. The policy is to pick the first idle CPU which is big enough for the task (task_util * margin < cpu_capacity). If no idle CPU is big enough, we pick the idle one with the highest capacity. Unlike other select_idle_sibling() helpers, this one operates on the sd_asym_cpucapacity sched_domain pointer, which is guaranteed to span all known CPU capacities in the system. As such, this will work for both "legacy" big.LITTLE (LITTLEs & bigs split at MC, joined at DIE) and for newer DynamIQ systems (e.g. LITTLEs and bigs in the same MC domain). Note that this limits the scope of select_idle_sibling() to select_idle_capacity() for asymmetric CPU capacity systems - the LLC domain will not be scanned, and no further heuristic will be applied. Signed-off-by: Morten Rasmussen Signed-off-by: Valentin Schneider Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Reviewed-by: Quentin Perret Link: https://lkml.kernel.org/r/20200206191957.12325-2-valentin.schneider@arm.com --- kernel/sched/fair.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1a0ce83e835a..6fb47a2f7383 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5896,6 +5896,40 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, int t return cpu; } +/* + * Scan the asym_capacity domain for idle CPUs; pick the first idle one on which + * the task fits. If no CPU is big enough, but there are idle ones, try to + * maximize capacity. + */ +static int +select_idle_capacity(struct task_struct *p, struct sched_domain *sd, int target) +{ + unsigned long best_cap = 0; + int cpu, best_cpu = -1; + struct cpumask *cpus; + + sync_entity_load_avg(&p->se); + + cpus = this_cpu_cpumask_var_ptr(select_idle_mask); + cpumask_and(cpus, sched_domain_span(sd), p->cpus_ptr); + + for_each_cpu_wrap(cpu, cpus, target) { + unsigned long cpu_cap = capacity_of(cpu); + + if (!available_idle_cpu(cpu) && !sched_idle_cpu(cpu)) + continue; + if (task_fits_capacity(p, cpu_cap)) + return cpu; + + if (cpu_cap > best_cap) { + best_cap = cpu_cap; + best_cpu = cpu; + } + } + + return best_cpu; +} + /* * Try and locate an idle core/thread in the LLC cache domain. */ @@ -5904,6 +5938,28 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target) struct sched_domain *sd; int i, recent_used_cpu; + /* + * For asymmetric CPU capacity systems, our domain of interest is + * sd_asym_cpucapacity rather than sd_llc. + */ + if (static_branch_unlikely(&sched_asym_cpucapacity)) { + sd = rcu_dereference(per_cpu(sd_asym_cpucapacity, target)); + /* + * On an asymmetric CPU capacity system where an exclusive + * cpuset defines a symmetric island (i.e. one unique + * capacity_orig value through the cpuset), the key will be set + * but the CPUs within that cpuset will not have a domain with + * SD_ASYM_CPUCAPACITY. These should follow the usual symmetric + * capacity path. + */ + if (!sd) + goto symmetric; + + i = select_idle_capacity(p, sd, target); + return ((unsigned)i < nr_cpumask_bits) ? i : target; + } + +symmetric: if (available_idle_cpu(target) || sched_idle_cpu(target)) return target; From a526d466798d65cff120ee00ef92931075bf3769 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Thu, 6 Feb 2020 19:19:55 +0000 Subject: [PATCH 0359/2231] sched/topology: Remove SD_BALANCE_WAKE on asymmetric capacity systems SD_BALANCE_WAKE was previously added to lower sched_domain levels on asymmetric CPU capacity systems by commit: 9ee1cda5ee25 ("sched/core: Enable SD_BALANCE_WAKE for asymmetric capacity systems") to enable the use of find_idlest_cpu() and friends to find an appropriate CPU for tasks. That responsibility has now been shifted to select_idle_sibling() and friends, and hence the flag can be removed. Note that this causes asymmetric CPU capacity systems to no longer enter the slow wakeup path (find_idlest_cpu()) on wakeups - only on execs and forks (which is aligned with all other mainline topologies). Signed-off-by: Morten Rasmussen [Changelog tweaks] Signed-off-by: Valentin Schneider Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Reviewed-by: Quentin Perret Link: https://lkml.kernel.org/r/20200206191957.12325-3-valentin.schneider@arm.com --- kernel/sched/topology.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index dfb64c08a407..00911884b7e7 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -1374,18 +1374,9 @@ sd_init(struct sched_domain_topology_level *tl, * Convert topological properties into behaviour. */ - if (sd->flags & SD_ASYM_CPUCAPACITY) { - struct sched_domain *t = sd; - - /* - * Don't attempt to spread across CPUs of different capacities. - */ - if (sd->child) - sd->child->flags &= ~SD_PREFER_SIBLING; - - for_each_lower_domain(t) - t->flags |= SD_BALANCE_WAKE; - } + /* Don't attempt to spread across CPUs of different capacities. */ + if ((sd->flags & SD_ASYM_CPUCAPACITY) && sd->child) + sd->child->flags &= ~SD_PREFER_SIBLING; if (sd->flags & SD_SHARE_CPUCAPACITY) { sd->imbalance_pct = 110; From f8459197e75b045d8d1d87b9856486b39e375721 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Thu, 6 Feb 2020 19:19:56 +0000 Subject: [PATCH 0360/2231] sched/core: Remove for_each_lower_domain() The last remaining user of this macro has just been removed, get rid of it. Suggested-by: Dietmar Eggemann Signed-off-by: Valentin Schneider Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Reviewed-by: Quentin Perret Link: https://lkml.kernel.org/r/20200206191957.12325-4-valentin.schneider@arm.com --- kernel/sched/sched.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 0844e81964e5..878910e8b299 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1337,8 +1337,6 @@ extern void sched_ttwu_pending(void); for (__sd = rcu_dereference_check_sched_domain(cpu_rq(cpu)->sd); \ __sd; __sd = __sd->parent) -#define for_each_lower_domain(sd) for (; sd; sd = sd->child) - /** * highest_flag_domain - Return highest sched_domain containing flag. * @cpu: The CPU whose highest level of sched domain is to From 000619680c3714020ce9db17eef6a4a7ce2dc28b Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Thu, 6 Feb 2020 19:19:57 +0000 Subject: [PATCH 0361/2231] sched/fair: Remove wake_cap() Capacity-awareness in the wake-up path previously involved disabling wake_affine in certain scenarios. We have just made select_idle_sibling() capacity-aware, so this isn't needed anymore. Remove wake_cap() entirely. Signed-off-by: Morten Rasmussen [Changelog tweaks] Signed-off-by: Valentin Schneider Signed-off-by: Peter Zijlstra (Intel) [Changelog tweaks] Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20200206191957.12325-5-valentin.schneider@arm.com --- kernel/sched/fair.c | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 6fb47a2f7383..a7e11b1bb64c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6145,33 +6145,6 @@ static unsigned long cpu_util_without(int cpu, struct task_struct *p) return min_t(unsigned long, util, capacity_orig_of(cpu)); } -/* - * Disable WAKE_AFFINE in the case where task @p doesn't fit in the - * capacity of either the waking CPU @cpu or the previous CPU @prev_cpu. - * - * In that case WAKE_AFFINE doesn't make sense and we'll let - * BALANCE_WAKE sort things out. - */ -static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) -{ - long min_cap, max_cap; - - if (!static_branch_unlikely(&sched_asym_cpucapacity)) - return 0; - - min_cap = min(capacity_orig_of(prev_cpu), capacity_orig_of(cpu)); - max_cap = cpu_rq(cpu)->rd->max_cpu_capacity; - - /* Minimum capacity is close to max, no need to abort wake_affine */ - if (max_cap - min_cap < max_cap >> 3) - return 0; - - /* Bring task utilization in sync with prev_cpu */ - sync_entity_load_avg(&p->se); - - return !task_fits_capacity(p, min_cap); -} - /* * Predicts what cpu_util(@cpu) would return if @p was migrated (and enqueued) * to @dst_cpu. @@ -6436,8 +6409,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f new_cpu = prev_cpu; } - want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu) && - cpumask_test_cpu(cpu, p->cpus_ptr); + want_affine = !wake_wide(p) && cpumask_test_cpu(cpu, p->cpus_ptr); } rcu_read_lock(); From 66630058e56b26b3a9cf2625e250a8c592dd0207 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 8 Feb 2020 20:48:29 +0100 Subject: [PATCH 0362/2231] sched/rt: Provide migrate_disable/enable() inlines Code which solely needs to prevent migration of a task uses preempt_disable()/enable() pairs. This is the only reliable way to do so as setting the task affinity to a single CPU can be undone by a setaffinity operation from a different task/process. RT provides a seperate migrate_disable/enable() mechanism which does not disable preemption to achieve the semantic requirements of a (almost) fully preemptible kernel. As it is unclear from looking at a given code path whether the intention is to disable preemption or migration, introduce migrate_disable/enable() inline functions which can be used to annotate code which merely needs to disable migration. Map them to preempt_disable/enable() for now. The RT substitution will be provided later. Code which is annotated that way documents that it has no requirement to protect against reentrancy of a preempting task. Either this is not required at all or the call sites are already serialized by other means. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: Juri Lelli Cc: Vincent Guittot Cc: Dietmar Eggemann Cc: Steven Rostedt Cc: Ben Segall Cc: Mel Gorman Cc: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/878slclv1u.fsf@nanos.tec.linutronix.de --- include/linux/preempt.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/linux/preempt.h b/include/linux/preempt.h index bbb68dba37cc..bc3f1aecaa19 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -322,4 +322,34 @@ static inline void preempt_notifier_init(struct preempt_notifier *notifier, #endif +/** + * migrate_disable - Prevent migration of the current task + * + * Maps to preempt_disable() which also disables preemption. Use + * migrate_disable() to annotate that the intent is to prevent migration, + * but not necessarily preemption. + * + * Can be invoked nested like preempt_disable() and needs the corresponding + * number of migrate_enable() invocations. + */ +static __always_inline void migrate_disable(void) +{ + preempt_disable(); +} + +/** + * migrate_enable - Allow migration of the current task + * + * Counterpart to migrate_disable(). + * + * As migrate_disable() can be invoked nested, only the outermost invocation + * reenables migration. + * + * Currently mapped to preempt_enable(). + */ +static __always_inline void migrate_enable(void) +{ + preempt_enable(); +} + #endif /* __LINUX_PREEMPT_H */ From 4e139c7711633365ebb52fbb63905395522a8413 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 14 Feb 2020 14:39:19 +0100 Subject: [PATCH 0363/2231] sched: Provide cant_migrate() Some code pathes rely on preempt_disable() to prevent migration on a non RT enabled kernel. These preempt_disable/enable() pairs are substituted by migrate_disable/enable() pairs or other forms of RT specific protection. On RT these protections prevent migration but not preemption. Obviously a cant_sleep() check in such a section will trigger on RT because preemption is not disabled. Provide a cant_migrate() macro which maps to cant_sleep() on a non RT kernel and an empty placeholder for RT for now. The placeholder will be changed to a proper debug check along with the RT specific migration protection mechanism. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20200214161503.070487511@linutronix.de --- include/linux/kernel.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 0d9db2a14f44..9b7a8d74a9d6 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -257,6 +257,13 @@ extern void __cant_sleep(const char *file, int line, int preempt_offset); #define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0) +#ifndef CONFIG_PREEMPT_RT +# define cant_migrate() cant_sleep() +#else + /* Placeholder for now */ +# define cant_migrate() do { } while (0) +#endif + /** * abs - return absolute value of an argument * @x: the value. If it is unsigned type, it is converted to signed type first. From 6650cdd9a8ccf00555dbbe743d58541ad8feb6a7 Mon Sep 17 00:00:00 2001 From: "Peter Zijlstra (Intel)" Date: Sun, 26 Jan 2020 12:05:35 -0800 Subject: [PATCH 0364/2231] x86/split_lock: Enable split lock detection by kernel A split-lock occurs when an atomic instruction operates on data that spans two cache lines. In order to maintain atomicity the core takes a global bus lock. This is typically >1000 cycles slower than an atomic operation within a cache line. It also disrupts performance on other cores (which must wait for the bus lock to be released before their memory operations can complete). For real-time systems this may mean missing deadlines. For other systems it may just be very annoying. Some CPUs have the capability to raise an #AC trap when a split lock is attempted. Provide a command line option to give the user choices on how to handle this: split_lock_detect= off - not enabled (no traps for split locks) warn - warn once when an application does a split lock, but allow it to continue running. fatal - Send SIGBUS to applications that cause split lock On systems that support split lock detection the default is "warn". Note that if the kernel hits a split lock in any mode other than "off" it will OOPs. One implementation wrinkle is that the MSR to control the split lock detection is per-core, not per thread. This might result in some short lived races on HT systems in "warn" mode if Linux tries to enable on one thread while disabling on the other. Race analysis by Sean Christopherson: - Toggling of split-lock is only done in "warn" mode. Worst case scenario of a race is that a misbehaving task will generate multiple #AC exceptions on the same instruction. And this race will only occur if both siblings are running tasks that generate split-lock #ACs, e.g. a race where sibling threads are writing different values will only occur if CPUx is disabling split-lock after an #AC and CPUy is re-enabling split-lock after *its* previous task generated an #AC. - Transitioning between off/warn/fatal modes at runtime isn't supported and disabling is tracked per task, so hardware will always reach a steady state that matches the configured mode. I.e. split-lock is guaranteed to be enabled in hardware once all _TIF_SLD threads have been scheduled out. Signed-off-by: Peter Zijlstra (Intel) Co-developed-by: Fenghua Yu Signed-off-by: Fenghua Yu Co-developed-by: Tony Luck Signed-off-by: Tony Luck Signed-off-by: Thomas Gleixner Signed-off-by: Borislav Petkov Link: https://lore.kernel.org/r/20200126200535.GB30377@agluck-desk2.amr.corp.intel.com --- .../admin-guide/kernel-parameters.txt | 22 +++ arch/x86/include/asm/cpu.h | 12 ++ arch/x86/include/asm/cpufeatures.h | 2 + arch/x86/include/asm/msr-index.h | 9 + arch/x86/include/asm/thread_info.h | 4 +- arch/x86/kernel/cpu/common.c | 2 + arch/x86/kernel/cpu/intel.c | 175 ++++++++++++++++++ arch/x86/kernel/process.c | 3 + arch/x86/kernel/traps.c | 24 ++- 9 files changed, 250 insertions(+), 3 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index dbc22d684627..62c2b0b6922e 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4655,6 +4655,28 @@ spia_pedr= spia_peddr= + split_lock_detect= + [X86] Enable split lock detection + + When enabled (and if hardware support is present), atomic + instructions that access data across cache line + boundaries will result in an alignment check exception. + + off - not enabled + + warn - the kernel will emit rate limited warnings + about applications triggering the #AC + exception. This mode is the default on CPUs + that supports split lock detection. + + fatal - the kernel will send SIGBUS to applications + that trigger the #AC exception. + + If an #AC exception is hit in the kernel or in + firmware (i.e. not while executing in user mode) + the kernel will oops in either "warn" or "fatal" + mode. + srcutree.counter_wrap_check [KNL] Specifies how frequently to check for grace-period sequence counter wrap for the diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index adc6cc86b062..ff6f3ca649b3 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -40,4 +40,16 @@ int mwait_usable(const struct cpuinfo_x86 *); unsigned int x86_family(unsigned int sig); unsigned int x86_model(unsigned int sig); unsigned int x86_stepping(unsigned int sig); +#ifdef CONFIG_CPU_SUP_INTEL +extern void __init cpu_set_core_cap_bits(struct cpuinfo_x86 *c); +extern void switch_to_sld(unsigned long tifn); +extern bool handle_user_split_lock(struct pt_regs *regs, long error_code); +#else +static inline void __init cpu_set_core_cap_bits(struct cpuinfo_x86 *c) {} +static inline void switch_to_sld(unsigned long tifn) {} +static inline bool handle_user_split_lock(struct pt_regs *regs, long error_code) +{ + return false; +} +#endif #endif /* _ASM_X86_CPU_H */ diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index f3327cb56edf..cd56ad5d308e 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -285,6 +285,7 @@ #define X86_FEATURE_CQM_MBM_LOCAL (11*32+ 3) /* LLC Local MBM monitoring */ #define X86_FEATURE_FENCE_SWAPGS_USER (11*32+ 4) /* "" LFENCE in user entry SWAPGS path */ #define X86_FEATURE_FENCE_SWAPGS_KERNEL (11*32+ 5) /* "" LFENCE in kernel entry SWAPGS path */ +#define X86_FEATURE_SPLIT_LOCK_DETECT (11*32+ 6) /* #AC for split lock */ /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */ #define X86_FEATURE_AVX512_BF16 (12*32+ 5) /* AVX512 BFLOAT16 instructions */ @@ -367,6 +368,7 @@ #define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */ #define X86_FEATURE_FLUSH_L1D (18*32+28) /* Flush L1D cache */ #define X86_FEATURE_ARCH_CAPABILITIES (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */ +#define X86_FEATURE_CORE_CAPABILITIES (18*32+30) /* "" IA32_CORE_CAPABILITIES MSR */ #define X86_FEATURE_SPEC_CTRL_SSBD (18*32+31) /* "" Speculative Store Bypass Disable */ /* diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index ebe1685e92dd..8821697a7549 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -41,6 +41,10 @@ /* Intel MSRs. Some also available on other CPUs */ +#define MSR_TEST_CTRL 0x00000033 +#define MSR_TEST_CTRL_SPLIT_LOCK_DETECT_BIT 29 +#define MSR_TEST_CTRL_SPLIT_LOCK_DETECT BIT(MSR_TEST_CTRL_SPLIT_LOCK_DETECT_BIT) + #define MSR_IA32_SPEC_CTRL 0x00000048 /* Speculation Control */ #define SPEC_CTRL_IBRS BIT(0) /* Indirect Branch Restricted Speculation */ #define SPEC_CTRL_STIBP_SHIFT 1 /* Single Thread Indirect Branch Predictor (STIBP) bit */ @@ -70,6 +74,11 @@ */ #define MSR_IA32_UMWAIT_CONTROL_TIME_MASK (~0x03U) +/* Abbreviated from Intel SDM name IA32_CORE_CAPABILITIES */ +#define MSR_IA32_CORE_CAPS 0x000000cf +#define MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT_BIT 5 +#define MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT BIT(MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT_BIT) + #define MSR_PKG_CST_CONFIG_CONTROL 0x000000e2 #define NHM_C3_AUTO_DEMOTE (1UL << 25) #define NHM_C1_AUTO_DEMOTE (1UL << 26) diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index cf4327986e98..f807930bd763 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -92,6 +92,7 @@ struct thread_info { #define TIF_NOCPUID 15 /* CPUID is not accessible in userland */ #define TIF_NOTSC 16 /* TSC is not accessible in userland */ #define TIF_IA32 17 /* IA32 compatibility process */ +#define TIF_SLD 18 /* Restore split lock detection on context switch */ #define TIF_NOHZ 19 /* in adaptive nohz mode */ #define TIF_MEMDIE 20 /* is terminating due to OOM killer */ #define TIF_POLLING_NRFLAG 21 /* idle is polling for TIF_NEED_RESCHED */ @@ -122,6 +123,7 @@ struct thread_info { #define _TIF_NOCPUID (1 << TIF_NOCPUID) #define _TIF_NOTSC (1 << TIF_NOTSC) #define _TIF_IA32 (1 << TIF_IA32) +#define _TIF_SLD (1 << TIF_SLD) #define _TIF_NOHZ (1 << TIF_NOHZ) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) @@ -145,7 +147,7 @@ struct thread_info { /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW_BASE \ (_TIF_NOCPUID | _TIF_NOTSC | _TIF_BLOCKSTEP | \ - _TIF_SSBD | _TIF_SPEC_FORCE_UPDATE) + _TIF_SSBD | _TIF_SPEC_FORCE_UPDATE | _TIF_SLD) /* * Avoid calls to __switch_to_xtra() on UP as STIBP is not evaluated. diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 52c9bfbbdb2a..bf282d7805de 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1224,6 +1224,8 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c) cpu_set_bug_bits(c); + cpu_set_core_cap_bits(c); + fpu__init_system(c); #ifdef CONFIG_X86_32 diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index be82cd5841c3..db3e745e5d47 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #ifdef CONFIG_X86_64 #include @@ -31,6 +33,19 @@ #include #endif +enum split_lock_detect_state { + sld_off = 0, + sld_warn, + sld_fatal, +}; + +/* + * Default to sld_off because most systems do not support split lock detection + * split_lock_setup() will switch this to sld_warn on systems that support + * split lock detect, unless there is a command line override. + */ +static enum split_lock_detect_state sld_state = sld_off; + /* * Processors which have self-snooping capability can handle conflicting * memory type across CPUs by snooping its own cache. However, there exists @@ -570,6 +585,8 @@ static void init_intel_misc_features(struct cpuinfo_x86 *c) wrmsrl(MSR_MISC_FEATURES_ENABLES, msr); } +static void split_lock_init(void); + static void init_intel(struct cpuinfo_x86 *c) { early_init_intel(c); @@ -684,6 +701,8 @@ static void init_intel(struct cpuinfo_x86 *c) tsx_enable(); if (tsx_ctrl_state == TSX_CTRL_DISABLE) tsx_disable(); + + split_lock_init(); } #ifdef CONFIG_X86_32 @@ -945,3 +964,159 @@ static const struct cpu_dev intel_cpu_dev = { }; cpu_dev_register(intel_cpu_dev); + +#undef pr_fmt +#define pr_fmt(fmt) "x86/split lock detection: " fmt + +static const struct { + const char *option; + enum split_lock_detect_state state; +} sld_options[] __initconst = { + { "off", sld_off }, + { "warn", sld_warn }, + { "fatal", sld_fatal }, +}; + +static inline bool match_option(const char *arg, int arglen, const char *opt) +{ + int len = strlen(opt); + + return len == arglen && !strncmp(arg, opt, len); +} + +static void __init split_lock_setup(void) +{ + char arg[20]; + int i, ret; + + setup_force_cpu_cap(X86_FEATURE_SPLIT_LOCK_DETECT); + sld_state = sld_warn; + + ret = cmdline_find_option(boot_command_line, "split_lock_detect", + arg, sizeof(arg)); + if (ret >= 0) { + for (i = 0; i < ARRAY_SIZE(sld_options); i++) { + if (match_option(arg, ret, sld_options[i].option)) { + sld_state = sld_options[i].state; + break; + } + } + } + + switch (sld_state) { + case sld_off: + pr_info("disabled\n"); + break; + + case sld_warn: + pr_info("warning about user-space split_locks\n"); + break; + + case sld_fatal: + pr_info("sending SIGBUS on user-space split_locks\n"); + break; + } +} + +/* + * Locking is not required at the moment because only bit 29 of this + * MSR is implemented and locking would not prevent that the operation + * of one thread is immediately undone by the sibling thread. + * Use the "safe" versions of rdmsr/wrmsr here because although code + * checks CPUID and MSR bits to make sure the TEST_CTRL MSR should + * exist, there may be glitches in virtualization that leave a guest + * with an incorrect view of real h/w capabilities. + */ +static bool __sld_msr_set(bool on) +{ + u64 test_ctrl_val; + + if (rdmsrl_safe(MSR_TEST_CTRL, &test_ctrl_val)) + return false; + + if (on) + test_ctrl_val |= MSR_TEST_CTRL_SPLIT_LOCK_DETECT; + else + test_ctrl_val &= ~MSR_TEST_CTRL_SPLIT_LOCK_DETECT; + + return !wrmsrl_safe(MSR_TEST_CTRL, test_ctrl_val); +} + +static void split_lock_init(void) +{ + if (sld_state == sld_off) + return; + + if (__sld_msr_set(true)) + return; + + /* + * If this is anything other than the boot-cpu, you've done + * funny things and you get to keep whatever pieces. + */ + pr_warn("MSR fail -- disabled\n"); + sld_state = sld_off; +} + +bool handle_user_split_lock(struct pt_regs *regs, long error_code) +{ + if ((regs->flags & X86_EFLAGS_AC) || sld_state == sld_fatal) + return false; + + pr_warn_ratelimited("#AC: %s/%d took a split_lock trap at address: 0x%lx\n", + current->comm, current->pid, regs->ip); + + /* + * Disable the split lock detection for this task so it can make + * progress and set TIF_SLD so the detection is re-enabled via + * switch_to_sld() when the task is scheduled out. + */ + __sld_msr_set(false); + set_tsk_thread_flag(current, TIF_SLD); + return true; +} + +/* + * This function is called only when switching between tasks with + * different split-lock detection modes. It sets the MSR for the + * mode of the new task. This is right most of the time, but since + * the MSR is shared by hyperthreads on a physical core there can + * be glitches when the two threads need different modes. + */ +void switch_to_sld(unsigned long tifn) +{ + __sld_msr_set(!(tifn & _TIF_SLD)); +} + +#define SPLIT_LOCK_CPU(model) {X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY} + +/* + * The following processors have the split lock detection feature. But + * since they don't have the IA32_CORE_CAPABILITIES MSR, the feature cannot + * be enumerated. Enable it by family and model matching on these + * processors. + */ +static const struct x86_cpu_id split_lock_cpu_ids[] __initconst = { + SPLIT_LOCK_CPU(INTEL_FAM6_ICELAKE_X), + SPLIT_LOCK_CPU(INTEL_FAM6_ICELAKE_L), + {} +}; + +void __init cpu_set_core_cap_bits(struct cpuinfo_x86 *c) +{ + u64 ia32_core_caps = 0; + + if (c->x86_vendor != X86_VENDOR_INTEL) + return; + if (cpu_has(c, X86_FEATURE_CORE_CAPABILITIES)) { + /* Enumerate features reported in IA32_CORE_CAPABILITIES MSR. */ + rdmsrl(MSR_IA32_CORE_CAPS, ia32_core_caps); + } else if (!boot_cpu_has(X86_FEATURE_HYPERVISOR)) { + /* Enumerate split lock detection by family and model. */ + if (x86_match_cpu(split_lock_cpu_ids)) + ia32_core_caps |= MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT; + } + + if (ia32_core_caps & MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT) + split_lock_setup(); +} diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 839b5244e3b7..a43c32868c3c 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -650,6 +650,9 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p) /* Enforce MSR update to ensure consistent state */ __speculation_ctrl_update(~tifn, tifn); } + + if ((tifp ^ tifn) & _TIF_SLD) + switch_to_sld(tifn); } /* diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 6ef00eb6fbb9..0ef5befaed7d 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -242,7 +243,6 @@ do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, { struct task_struct *tsk = current; - if (!do_trap_no_signal(tsk, trapnr, str, regs, error_code)) return; @@ -288,9 +288,29 @@ DO_ERROR(X86_TRAP_OLD_MF, SIGFPE, 0, NULL, "coprocessor segment overru DO_ERROR(X86_TRAP_TS, SIGSEGV, 0, NULL, "invalid TSS", invalid_TSS) DO_ERROR(X86_TRAP_NP, SIGBUS, 0, NULL, "segment not present", segment_not_present) DO_ERROR(X86_TRAP_SS, SIGBUS, 0, NULL, "stack segment", stack_segment) -DO_ERROR(X86_TRAP_AC, SIGBUS, BUS_ADRALN, NULL, "alignment check", alignment_check) #undef IP +dotraplinkage void do_alignment_check(struct pt_regs *regs, long error_code) +{ + char *str = "alignment check"; + + RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); + + if (notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_AC, SIGBUS) == NOTIFY_STOP) + return; + + if (!user_mode(regs)) + die("Split lock detected\n", regs, error_code); + + local_irq_enable(); + + if (handle_user_split_lock(regs, error_code)) + return; + + do_trap(X86_TRAP_AC, SIGBUS, "alignment check", regs, + error_code, BUS_ADRALN, NULL); +} + #ifdef CONFIG_VMAP_STACK __visible void __noreturn handle_stack_overflow(const char *message, struct pt_regs *regs, From 9f01eb5d4936f12d57da84cdfbc2a3623e23a7eb Mon Sep 17 00:00:00 2001 From: Madhuparna Bhowmik Date: Tue, 10 Dec 2019 11:16:39 +0530 Subject: [PATCH 0365/2231] nfs: Fix nfs_access_get_cached_rcu() sparse error This patch fixes the following sparse error: fs/nfs/dir.c:2353:14: error: incompatible types in comparison expression (different address spaces): fs/nfs/dir.c:2353:14: struct list_head [noderef] * fs/nfs/dir.c:2353:14: struct list_head * Signed-off-by: Madhuparna Bhowmik Signed-off-by: Paul E. McKenney --- fs/nfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 1320288ff9ec..55a29b0d52fc 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2383,7 +2383,7 @@ static int nfs_access_get_cached_rcu(struct inode *inode, const struct cred *cre rcu_read_lock(); if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS) goto out; - lh = rcu_dereference(nfsi->access_cache_entry_lru.prev); + lh = rcu_dereference(list_tail_rcu(&nfsi->access_cache_entry_lru)); cache = list_entry(lh, struct nfs_access_entry, lru); if (lh == &nfsi->access_cache_entry_lru || cred_fscmp(cred, cache->cred) != 0) From 82dd8419e225958f01708cda8a3fc6c3c5356228 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 15 Dec 2019 11:38:57 -0800 Subject: [PATCH 0366/2231] rcu: Warn on for_each_leaf_node_cpu_mask() from non-leaf The for_each_leaf_node_cpu_mask() and for_each_leaf_node_possible_cpu() macros must be invoked only on leaf rcu_node structures. Failing to abide by this restriction can result in infinite loops on systems with more than 64 CPUs (or for more than 32 CPUs on 32-bit systems). This commit therefore adds WARN_ON_ONCE() calls to make misuse of these two macros easier to debug. Reported-by: Qian Cai Signed-off-by: Paul E. McKenney --- kernel/rcu/rcu.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h index 05f936ed167a..f6ce173e35f6 100644 --- a/kernel/rcu/rcu.h +++ b/kernel/rcu/rcu.h @@ -325,7 +325,8 @@ static inline void rcu_init_levelspread(int *levelspread, const int *levelcnt) * Iterate over all possible CPUs in a leaf RCU node. */ #define for_each_leaf_node_possible_cpu(rnp, cpu) \ - for ((cpu) = cpumask_next((rnp)->grplo - 1, cpu_possible_mask); \ + for (WARN_ON_ONCE(!rcu_is_leaf_node(rnp)), \ + (cpu) = cpumask_next((rnp)->grplo - 1, cpu_possible_mask); \ (cpu) <= rnp->grphi; \ (cpu) = cpumask_next((cpu), cpu_possible_mask)) @@ -335,7 +336,8 @@ static inline void rcu_init_levelspread(int *levelspread, const int *levelcnt) #define rcu_find_next_bit(rnp, cpu, mask) \ ((rnp)->grplo + find_next_bit(&(mask), BITS_PER_LONG, (cpu))) #define for_each_leaf_node_cpu_mask(rnp, cpu, mask) \ - for ((cpu) = rcu_find_next_bit((rnp), 0, (mask)); \ + for (WARN_ON_ONCE(!rcu_is_leaf_node(rnp)), \ + (cpu) = rcu_find_next_bit((rnp), 0, (mask)); \ (cpu) <= rnp->grphi; \ (cpu) = rcu_find_next_bit((rnp), (cpu) + 1 - (rnp->grplo), (mask))) From 24bb9eccf7ff335c16c2970ac7cd5c32a92821a6 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 22 Dec 2019 19:55:50 -0800 Subject: [PATCH 0367/2231] rcu: Fix exp_funnel_lock()/rcu_exp_wait_wake() datarace The rcu_node structure's ->exp_seq_rq field is accessed locklessly, so updates must use WRITE_ONCE(). This commit therefore adds the needed WRITE_ONCE() invocation where it was missed. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_exp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index dcbd75791f39..d7e04849c7ab 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -589,7 +589,7 @@ static void rcu_exp_wait_wake(unsigned long s) spin_lock(&rnp->exp_lock); /* Recheck, avoid hang in case someone just arrived. */ if (ULONG_CMP_LT(rnp->exp_seq_rq, s)) - rnp->exp_seq_rq = s; + WRITE_ONCE(rnp->exp_seq_rq, s); spin_unlock(&rnp->exp_lock); } smp_mb(); /* All above changes before wakeup. */ From 8a7e8f51714004112a0bbbf751f3dd0fcbbbc983 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 2 Jan 2020 16:48:05 -0800 Subject: [PATCH 0368/2231] rcu: Provide debug symbols and line numbers in KCSAN runs This commit adds "-g -fno-omit-frame-pointer" to ease interpretation of KCSAN output, but only for CONFIG_KCSAN=y kerrnels. Signed-off-by: Paul E. McKenney --- kernel/rcu/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/rcu/Makefile b/kernel/rcu/Makefile index 82d5fba48b2f..f91f2c2cf138 100644 --- a/kernel/rcu/Makefile +++ b/kernel/rcu/Makefile @@ -3,6 +3,10 @@ # and is generally not a function of system call inputs. KCOV_INSTRUMENT := n +ifeq ($(CONFIG_KCSAN),y) +KBUILD_CFLAGS += -g -fno-omit-frame-pointer +endif + obj-y += update.o sync.o obj-$(CONFIG_TREE_SRCU) += srcutree.o obj-$(CONFIG_TINY_SRCU) += srcutiny.o From 7672d647ddae37d2ea267159950bcc311e962434 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 11:38:51 -0800 Subject: [PATCH 0369/2231] rcu: Add WRITE_ONCE() to rcu_node ->qsmask update The rcu_node structure's ->qsmask field is read locklessly, so this commit adds the WRITE_ONCE() to an update in order to provide proper documentation and READ_ONCE()/WRITE_ONCE() pairing. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index d91c9156fab2..bb57c24dbe9a 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1881,7 +1881,7 @@ static void rcu_report_qs_rnp(unsigned long mask, struct rcu_node *rnp, WARN_ON_ONCE(oldmask); /* Any child must be all zeroed! */ WARN_ON_ONCE(!rcu_is_leaf_node(rnp) && rcu_preempt_blocked_readers_cgp(rnp)); - rnp->qsmask &= ~mask; + WRITE_ONCE(rnp->qsmask, rnp->qsmask & ~mask); trace_rcu_quiescent_state_report(rcu_state.name, rnp->gp_seq, mask, rnp->qsmask, rnp->level, rnp->grplo, rnp->grphi, From b0c18c87730a4de6da0303fa99aea43e814233f9 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 12:12:06 -0800 Subject: [PATCH 0370/2231] rcu: Add WRITE_ONCE to rcu_node ->exp_seq_rq store The rcu_node structure's ->exp_seq_rq field is read locklessly, so this commit adds the WRITE_ONCE() to a load in order to provide proper documentation and READ_ONCE()/WRITE_ONCE() pairing. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_exp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index d7e04849c7ab..85b009e05637 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -314,7 +314,7 @@ static bool exp_funnel_lock(unsigned long s) sync_exp_work_done(s)); return true; } - rnp->exp_seq_rq = s; /* Followers can wait on us. */ + WRITE_ONCE(rnp->exp_seq_rq, s); /* Followers can wait on us. */ spin_unlock(&rnp->exp_lock); trace_rcu_exp_funnel_lock(rcu_state.name, rnp->level, rnp->grplo, rnp->grphi, TPS("nxtlvl")); From 0937d045732b5dd5e5df1fb6355d5f4e01f3c4d7 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 14:53:31 -0800 Subject: [PATCH 0371/2231] rcu: Add READ_ONCE() to rcu_node ->gp_seq The rcu_node structure's ->gp_seq field is read locklessly, so this commit adds the READ_ONCE() to several loads in order to avoid destructive compiler optimizations. This data race was reported by KCSAN. Not appropriate for backporting because this affects only tracing and warnings. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index bb57c24dbe9a..236692d7762f 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1126,8 +1126,9 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp) static void trace_rcu_this_gp(struct rcu_node *rnp, struct rcu_data *rdp, unsigned long gp_seq_req, const char *s) { - trace_rcu_future_grace_period(rcu_state.name, rnp->gp_seq, gp_seq_req, - rnp->level, rnp->grplo, rnp->grphi, s); + trace_rcu_future_grace_period(rcu_state.name, READ_ONCE(rnp->gp_seq), + gp_seq_req, rnp->level, + rnp->grplo, rnp->grphi, s); } /* @@ -1904,7 +1905,7 @@ static void rcu_report_qs_rnp(unsigned long mask, struct rcu_node *rnp, rnp_c = rnp; rnp = rnp->parent; raw_spin_lock_irqsave_rcu_node(rnp, flags); - oldmask = rnp_c->qsmask; + oldmask = READ_ONCE(rnp_c->qsmask); } /* @@ -2052,7 +2053,7 @@ int rcutree_dying_cpu(unsigned int cpu) return 0; blkd = !!(rnp->qsmask & rdp->grpmask); - trace_rcu_grace_period(rcu_state.name, rnp->gp_seq, + trace_rcu_grace_period(rcu_state.name, READ_ONCE(rnp->gp_seq), blkd ? TPS("cpuofl") : TPS("cpuofl-bgp")); return 0; } From 2906d2154cd6ad6e4452cc25315d3cea7bb7f2d7 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 15:17:12 -0800 Subject: [PATCH 0372/2231] rcu: Add WRITE_ONCE() to rcu_state ->gp_req_activity The rcu_state structure's ->gp_req_activity field is read locklessly, so this commit adds the WRITE_ONCE() to an update in order to provide proper documentation and READ_ONCE()/WRITE_ONCE() pairing. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 236692d7762f..944390981aed 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1200,7 +1200,7 @@ static bool rcu_start_this_gp(struct rcu_node *rnp_start, struct rcu_data *rdp, } trace_rcu_this_gp(rnp, rdp, gp_seq_req, TPS("Startedroot")); WRITE_ONCE(rcu_state.gp_flags, rcu_state.gp_flags | RCU_GP_FLAG_INIT); - rcu_state.gp_req_activity = jiffies; + WRITE_ONCE(rcu_state.gp_req_activity, jiffies); if (!rcu_state.gp_kthread) { trace_rcu_this_gp(rnp, rdp, gp_seq_req, TPS("NoGPkthread")); goto unlock_out; @@ -1775,7 +1775,7 @@ static void rcu_gp_cleanup(void) rcu_segcblist_is_offloaded(&rdp->cblist); if ((offloaded || !rcu_accelerate_cbs(rnp, rdp)) && needgp) { WRITE_ONCE(rcu_state.gp_flags, RCU_GP_FLAG_INIT); - rcu_state.gp_req_activity = jiffies; + WRITE_ONCE(rcu_state.gp_req_activity, jiffies); trace_rcu_grace_period(rcu_state.name, READ_ONCE(rcu_state.gp_seq), TPS("newreq")); From 105abf82b0a62664edabcd4c5b4d9414c74ef399 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 15:44:23 -0800 Subject: [PATCH 0373/2231] rcu: Add WRITE_ONCE() to rcu_node ->qsmaskinitnext The rcu_state structure's ->qsmaskinitnext field is read locklessly, so this commit adds the WRITE_ONCE() to an update in order to provide proper documentation and READ_ONCE()/WRITE_ONCE() pairing. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely for systems not doing incessant CPU-hotplug operations. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 944390981aed..346321aa3c90 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -3379,7 +3379,7 @@ void rcu_cpu_starting(unsigned int cpu) rnp = rdp->mynode; mask = rdp->grpmask; raw_spin_lock_irqsave_rcu_node(rnp, flags); - rnp->qsmaskinitnext |= mask; + WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext | mask); oldmask = rnp->expmaskinitnext; rnp->expmaskinitnext |= mask; oldmask ^= rnp->expmaskinitnext; @@ -3432,7 +3432,7 @@ void rcu_report_dead(unsigned int cpu) rcu_report_qs_rnp(mask, rnp, rnp->gp_seq, flags); raw_spin_lock_irqsave_rcu_node(rnp, flags); } - rnp->qsmaskinitnext &= ~mask; + WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext & ~mask); raw_spin_unlock_irqrestore_rcu_node(rnp, flags); raw_spin_unlock(&rcu_state.ofl_lock); From 0050c7b2d27c3cc126df59bd8094fb3d25b00ade Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 15:59:12 -0800 Subject: [PATCH 0374/2231] locking/rtmutex: rcu: Add WRITE_ONCE() to rt_mutex ->owner The rt_mutex structure's ->owner field is read locklessly, so this commit adds the WRITE_ONCE() to an update in order to provide proper documentation and READ_ONCE()/WRITE_ONCE() pairing. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Will Deacon --- kernel/locking/rtmutex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index 851bbb10819d..c9f090d64f00 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -57,7 +57,7 @@ rt_mutex_set_owner(struct rt_mutex *lock, struct task_struct *owner) if (rt_mutex_has_waiters(lock)) val |= RT_MUTEX_HAS_WAITERS; - lock->owner = (struct task_struct *)val; + WRITE_ONCE(lock->owner, (struct task_struct *)val); } static inline void clear_rt_mutex_waiters(struct rt_mutex *lock) From bfeebe24212d374f82bbf5b005371fe13acabb93 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 16:14:08 -0800 Subject: [PATCH 0375/2231] rcu: Add READ_ONCE() to rcu_segcblist ->tails[] The rcu_segcblist structure's ->tails[] array entries are read locklessly, so this commit adds the READ_ONCE() to a load in order to avoid destructive compiler optimizations. This data race was reported by KCSAN. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcu_segcblist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/rcu_segcblist.c b/kernel/rcu/rcu_segcblist.c index 5f4fd3b8777c..426a472e7308 100644 --- a/kernel/rcu/rcu_segcblist.c +++ b/kernel/rcu/rcu_segcblist.c @@ -182,7 +182,7 @@ void rcu_segcblist_offload(struct rcu_segcblist *rsclp) bool rcu_segcblist_ready_cbs(struct rcu_segcblist *rsclp) { return rcu_segcblist_is_enabled(rsclp) && - &rsclp->head != rsclp->tails[RCU_DONE_TAIL]; + &rsclp->head != READ_ONCE(rsclp->tails[RCU_DONE_TAIL]); } /* From 8ff37290d6622e130553a38ec2662a728e46cdba Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 4 Jan 2020 11:33:17 -0800 Subject: [PATCH 0376/2231] rcu: Add *_ONCE() for grace-period progress indicators The various RCU structures' ->gp_seq, ->gp_seq_needed, ->gp_req_activity, and ->gp_activity fields are read locklessly, so they must be updated with WRITE_ONCE() and, when read locklessly, with READ_ONCE(). This commit makes these changes. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 14 +++++++------- kernel/rcu/tree_plugin.h | 2 +- kernel/rcu/tree_stall.h | 26 +++++++++++++++----------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 346321aa3c90..53946b169699 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1175,7 +1175,7 @@ static bool rcu_start_this_gp(struct rcu_node *rnp_start, struct rcu_data *rdp, TPS("Prestarted")); goto unlock_out; } - rnp->gp_seq_needed = gp_seq_req; + WRITE_ONCE(rnp->gp_seq_needed, gp_seq_req); if (rcu_seq_state(rcu_seq_current(&rnp->gp_seq))) { /* * We just marked the leaf or internal node, and a @@ -1210,8 +1210,8 @@ static bool rcu_start_this_gp(struct rcu_node *rnp_start, struct rcu_data *rdp, unlock_out: /* Push furthest requested GP to leaf node and rcu_data structure. */ if (ULONG_CMP_LT(gp_seq_req, rnp->gp_seq_needed)) { - rnp_start->gp_seq_needed = rnp->gp_seq_needed; - rdp->gp_seq_needed = rnp->gp_seq_needed; + WRITE_ONCE(rnp_start->gp_seq_needed, rnp->gp_seq_needed); + WRITE_ONCE(rdp->gp_seq_needed, rnp->gp_seq_needed); } if (rnp != rnp_start) raw_spin_unlock_rcu_node(rnp); @@ -1423,7 +1423,7 @@ static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp) } rdp->gp_seq = rnp->gp_seq; /* Remember new grace-period state. */ if (ULONG_CMP_LT(rdp->gp_seq_needed, rnp->gp_seq_needed) || rdp->gpwrap) - rdp->gp_seq_needed = rnp->gp_seq_needed; + WRITE_ONCE(rdp->gp_seq_needed, rnp->gp_seq_needed); WRITE_ONCE(rdp->gpwrap, false); rcu_gpnum_ovf(rnp, rdp); return ret; @@ -3276,12 +3276,12 @@ int rcutree_prepare_cpu(unsigned int cpu) rnp = rdp->mynode; raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */ rdp->beenonline = true; /* We have now been online. */ - rdp->gp_seq = rnp->gp_seq; - rdp->gp_seq_needed = rnp->gp_seq; + rdp->gp_seq = READ_ONCE(rnp->gp_seq); + rdp->gp_seq_needed = rdp->gp_seq; rdp->cpu_no_qs.b.norm = true; rdp->core_needs_qs = false; rdp->rcu_iw_pending = false; - rdp->rcu_iw_gp_seq = rnp->gp_seq - 1; + rdp->rcu_iw_gp_seq = rdp->gp_seq - 1; trace_rcu_grace_period(rcu_state.name, rdp->gp_seq, TPS("cpuonl")); raw_spin_unlock_irqrestore_rcu_node(rnp, flags); rcu_prepare_kthreads(cpu); diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index c6ea81cd4189..b5ba14864015 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -753,7 +753,7 @@ dump_blkd_tasks(struct rcu_node *rnp, int ncheck) raw_lockdep_assert_held_rcu_node(rnp); pr_info("%s: grp: %d-%d level: %d ->gp_seq %ld ->completedqs %ld\n", __func__, rnp->grplo, rnp->grphi, rnp->level, - (long)rnp->gp_seq, (long)rnp->completedqs); + (long)READ_ONCE(rnp->gp_seq), (long)rnp->completedqs); for (rnp1 = rnp; rnp1; rnp1 = rnp1->parent) pr_info("%s: %d:%d ->qsmask %#lx ->qsmaskinit %#lx ->qsmaskinitnext %#lx\n", __func__, rnp1->grplo, rnp1->grphi, rnp1->qsmask, rnp1->qsmaskinit, rnp1->qsmaskinitnext); diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index 55f9b84790d3..43dc688c3785 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h @@ -592,21 +592,22 @@ void show_rcu_gp_kthreads(void) (long)READ_ONCE(rcu_get_root()->gp_seq_needed), READ_ONCE(rcu_state.gp_flags)); rcu_for_each_node_breadth_first(rnp) { - if (ULONG_CMP_GE(rcu_state.gp_seq, rnp->gp_seq_needed)) + if (ULONG_CMP_GE(READ_ONCE(rcu_state.gp_seq), + READ_ONCE(rnp->gp_seq_needed))) continue; pr_info("\trcu_node %d:%d ->gp_seq %ld ->gp_seq_needed %ld\n", - rnp->grplo, rnp->grphi, (long)rnp->gp_seq, - (long)rnp->gp_seq_needed); + rnp->grplo, rnp->grphi, (long)READ_ONCE(rnp->gp_seq), + (long)READ_ONCE(rnp->gp_seq_needed)); if (!rcu_is_leaf_node(rnp)) continue; for_each_leaf_node_possible_cpu(rnp, cpu) { rdp = per_cpu_ptr(&rcu_data, cpu); if (rdp->gpwrap || - ULONG_CMP_GE(rcu_state.gp_seq, - rdp->gp_seq_needed)) + ULONG_CMP_GE(READ_ONCE(rcu_state.gp_seq), + READ_ONCE(rdp->gp_seq_needed))) continue; pr_info("\tcpu %d ->gp_seq_needed %ld\n", - cpu, (long)rdp->gp_seq_needed); + cpu, (long)READ_ONCE(rdp->gp_seq_needed)); } } for_each_possible_cpu(cpu) { @@ -631,7 +632,8 @@ static void rcu_check_gp_start_stall(struct rcu_node *rnp, struct rcu_data *rdp, static atomic_t warned = ATOMIC_INIT(0); if (!IS_ENABLED(CONFIG_PROVE_RCU) || rcu_gp_in_progress() || - ULONG_CMP_GE(rnp_root->gp_seq, rnp_root->gp_seq_needed)) + ULONG_CMP_GE(READ_ONCE(rnp_root->gp_seq), + READ_ONCE(rnp_root->gp_seq_needed))) return; j = jiffies; /* Expensive access, and in common case don't get here. */ if (time_before(j, READ_ONCE(rcu_state.gp_req_activity) + gpssdelay) || @@ -642,7 +644,8 @@ static void rcu_check_gp_start_stall(struct rcu_node *rnp, struct rcu_data *rdp, raw_spin_lock_irqsave_rcu_node(rnp, flags); j = jiffies; if (rcu_gp_in_progress() || - ULONG_CMP_GE(rnp_root->gp_seq, rnp_root->gp_seq_needed) || + ULONG_CMP_GE(READ_ONCE(rnp_root->gp_seq), + READ_ONCE(rnp_root->gp_seq_needed)) || time_before(j, READ_ONCE(rcu_state.gp_req_activity) + gpssdelay) || time_before(j, READ_ONCE(rcu_state.gp_activity) + gpssdelay) || atomic_read(&warned)) { @@ -655,9 +658,10 @@ static void rcu_check_gp_start_stall(struct rcu_node *rnp, struct rcu_data *rdp, raw_spin_lock_rcu_node(rnp_root); /* irqs already disabled. */ j = jiffies; if (rcu_gp_in_progress() || - ULONG_CMP_GE(rnp_root->gp_seq, rnp_root->gp_seq_needed) || - time_before(j, rcu_state.gp_req_activity + gpssdelay) || - time_before(j, rcu_state.gp_activity + gpssdelay) || + ULONG_CMP_GE(READ_ONCE(rnp_root->gp_seq), + READ_ONCE(rnp_root->gp_seq_needed)) || + time_before(j, READ_ONCE(rcu_state.gp_req_activity) + gpssdelay) || + time_before(j, READ_ONCE(rcu_state.gp_activity) + gpssdelay) || atomic_xchg(&warned, 1)) { if (rnp_root != rnp) /* irqs remain disabled. */ From 65bb0dc437c3e57a6cde2b81170c8af4b9c90735 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 6 Jan 2020 21:08:02 +0100 Subject: [PATCH 0377/2231] rcu: Fix typos in file-header comments Convert to plural and add a note that this is for Tree RCU. Signed-off-by: SeongJae Park Signed-off-by: Paul E. McKenney --- kernel/rcu/srcutree.c | 2 +- kernel/rcu/tree.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index 657e6a7d1c03..7ddb29cc7dba 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -5,7 +5,7 @@ * Copyright (C) IBM Corporation, 2006 * Copyright (C) Fujitsu, 2012 * - * Author: Paul McKenney + * Authors: Paul McKenney * Lai Jiangshan * * For detailed explanation of Read-Copy Update mechanism see - diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 53946b169699..a70f56bb56a7 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Read-Copy Update mechanism for mutual exclusion + * Read-Copy Update mechanism for mutual exclusion (tree-based version) * * Copyright IBM Corporation, 2008 * * Authors: Dipankar Sarma * Manfred Spraul - * Paul E. McKenney Hierarchical version + * Paul E. McKenney * * Based on the original work by Paul McKenney * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. From a5b8950180f8e5acb802d1672e0b4d0ceee6126e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 7 Jan 2020 15:48:39 -0800 Subject: [PATCH 0378/2231] rcu: Add READ_ONCE() to rcu_data ->gpwrap The rcu_data structure's ->gpwrap field is read locklessly, and so this commit adds the required READ_ONCE() to a pair of laods in order to avoid destructive compiler optimizations. This data race was reported by KCSAN. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 2 +- kernel/rcu/tree_stall.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index a70f56bb56a7..e851a12920e6 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1322,7 +1322,7 @@ static void rcu_accelerate_cbs_unlocked(struct rcu_node *rnp, rcu_lockdep_assert_cblist_protected(rdp); c = rcu_seq_snap(&rcu_state.gp_seq); - if (!rdp->gpwrap && ULONG_CMP_GE(rdp->gp_seq_needed, c)) { + if (!READ_ONCE(rdp->gpwrap) && ULONG_CMP_GE(rdp->gp_seq_needed, c)) { /* Old request still live, so mark recent callbacks. */ (void)rcu_segcblist_accelerate(&rdp->cblist, c); return; diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index 43dc688c3785..bca637b274fb 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h @@ -602,7 +602,7 @@ void show_rcu_gp_kthreads(void) continue; for_each_leaf_node_possible_cpu(rnp, cpu) { rdp = per_cpu_ptr(&rcu_data, cpu); - if (rdp->gpwrap || + if (READ_ONCE(rdp->gpwrap) || ULONG_CMP_GE(READ_ONCE(rcu_state.gp_seq), READ_ONCE(rdp->gp_seq_needed))) continue; From 2a2ae872ef7aa958f2152b8b24c6e94cf5f1d0df Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 8 Jan 2020 20:06:25 -0800 Subject: [PATCH 0379/2231] rcu: Add *_ONCE() to rcu_data ->rcu_forced_tick The rcu_data structure's ->rcu_forced_tick field is read locklessly, so this commit adds WRITE_ONCE() to all updates and READ_ONCE() to all lockless reads. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index e851a12920e6..be59a5d7299d 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -818,11 +818,12 @@ static __always_inline void rcu_nmi_enter_common(bool irq) incby = 1; } else if (tick_nohz_full_cpu(rdp->cpu) && rdp->dynticks_nmi_nesting == DYNTICK_IRQ_NONIDLE && - READ_ONCE(rdp->rcu_urgent_qs) && !rdp->rcu_forced_tick) { + READ_ONCE(rdp->rcu_urgent_qs) && + !READ_ONCE(rdp->rcu_forced_tick)) { raw_spin_lock_rcu_node(rdp->mynode); // Recheck under lock. if (rdp->rcu_urgent_qs && !rdp->rcu_forced_tick) { - rdp->rcu_forced_tick = true; + WRITE_ONCE(rdp->rcu_forced_tick, true); tick_dep_set_cpu(rdp->cpu, TICK_DEP_BIT_RCU); } raw_spin_unlock_rcu_node(rdp->mynode); @@ -899,7 +900,7 @@ static void rcu_disable_urgency_upon_qs(struct rcu_data *rdp) WRITE_ONCE(rdp->rcu_need_heavy_qs, false); if (tick_nohz_full_cpu(rdp->cpu) && rdp->rcu_forced_tick) { tick_dep_clear_cpu(rdp->cpu, TICK_DEP_BIT_RCU); - rdp->rcu_forced_tick = false; + WRITE_ONCE(rdp->rcu_forced_tick, false); } } From 3ca3b0e2cbe0050d1777a22b7fc13cad620eb2ba Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 8 Jan 2020 20:12:59 -0800 Subject: [PATCH 0380/2231] rcu: Add *_ONCE() to rcu_node ->boost_kthread_status The rcu_node structure's ->boost_kthread_status field is accessed locklessly, so this commit causes all updates to use WRITE_ONCE() and all reads to use READ_ONCE(). This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_plugin.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index b5ba14864015..0f8b714f09f5 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1032,18 +1032,18 @@ static int rcu_boost_kthread(void *arg) trace_rcu_utilization(TPS("Start boost kthread@init")); for (;;) { - rnp->boost_kthread_status = RCU_KTHREAD_WAITING; + WRITE_ONCE(rnp->boost_kthread_status, RCU_KTHREAD_WAITING); trace_rcu_utilization(TPS("End boost kthread@rcu_wait")); rcu_wait(rnp->boost_tasks || rnp->exp_tasks); trace_rcu_utilization(TPS("Start boost kthread@rcu_wait")); - rnp->boost_kthread_status = RCU_KTHREAD_RUNNING; + WRITE_ONCE(rnp->boost_kthread_status, RCU_KTHREAD_RUNNING); more2boost = rcu_boost(rnp); if (more2boost) spincnt++; else spincnt = 0; if (spincnt > 10) { - rnp->boost_kthread_status = RCU_KTHREAD_YIELDING; + WRITE_ONCE(rnp->boost_kthread_status, RCU_KTHREAD_YIELDING); trace_rcu_utilization(TPS("End boost kthread@rcu_yield")); schedule_timeout_interruptible(2); trace_rcu_utilization(TPS("Start boost kthread@rcu_yield")); @@ -1082,7 +1082,7 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) rnp->boost_tasks = rnp->gp_tasks; raw_spin_unlock_irqrestore_rcu_node(rnp, flags); rcu_wake_cond(rnp->boost_kthread_task, - rnp->boost_kthread_status); + READ_ONCE(rnp->boost_kthread_status)); } else { raw_spin_unlock_irqrestore_rcu_node(rnp, flags); } From 90c018942c2babab73814648b37808fc3bf2ed1a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 7 Nov 2019 11:37:38 -0800 Subject: [PATCH 0381/2231] timer: Use hlist_unhashed_lockless() in timer_pending() The timer_pending() function is mostly used in lockless contexts, so Without proper annotations, KCSAN might detect a data-race [1]. Using hlist_unhashed_lockless() instead of hand-coding it seems appropriate (as suggested by Paul E. McKenney). [1] BUG: KCSAN: data-race in del_timer / detach_if_pending write to 0xffff88808697d870 of 8 bytes by task 10 on cpu 0: __hlist_del include/linux/list.h:764 [inline] detach_timer kernel/time/timer.c:815 [inline] detach_if_pending+0xcd/0x2d0 kernel/time/timer.c:832 try_to_del_timer_sync+0x60/0xb0 kernel/time/timer.c:1226 del_timer_sync+0x6b/0xa0 kernel/time/timer.c:1365 schedule_timeout+0x2d2/0x6e0 kernel/time/timer.c:1896 rcu_gp_fqs_loop+0x37c/0x580 kernel/rcu/tree.c:1639 rcu_gp_kthread+0x143/0x230 kernel/rcu/tree.c:1799 kthread+0x1d4/0x200 drivers/block/aoe/aoecmd.c:1253 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:352 read to 0xffff88808697d870 of 8 bytes by task 12060 on cpu 1: del_timer+0x3b/0xb0 kernel/time/timer.c:1198 sk_stop_timer+0x25/0x60 net/core/sock.c:2845 inet_csk_clear_xmit_timers+0x69/0xa0 net/ipv4/inet_connection_sock.c:523 tcp_clear_xmit_timers include/net/tcp.h:606 [inline] tcp_v4_destroy_sock+0xa3/0x3f0 net/ipv4/tcp_ipv4.c:2096 inet_csk_destroy_sock+0xf4/0x250 net/ipv4/inet_connection_sock.c:836 tcp_close+0x6f3/0x970 net/ipv4/tcp.c:2497 inet_release+0x86/0x100 net/ipv4/af_inet.c:427 __sock_release+0x85/0x160 net/socket.c:590 sock_close+0x24/0x30 net/socket.c:1268 __fput+0x1e1/0x520 fs/file_table.c:280 ____fput+0x1f/0x30 fs/file_table.c:313 task_work_run+0xf6/0x130 kernel/task_work.c:113 tracehook_notify_resume include/linux/tracehook.h:188 [inline] exit_to_usermode_loop+0x2b4/0x2c0 arch/x86/entry/common.c:163 Reported by Kernel Concurrency Sanitizer on: CPU: 1 PID: 12060 Comm: syz-executor.5 Not tainted 5.4.0-rc3+ #0 Hardware name: Google Google Compute Engine/Google Compute Engine, Signed-off-by: Eric Dumazet Cc: Thomas Gleixner [ paulmck: Pulled in Eric's later amendments. ] Signed-off-by: Paul E. McKenney --- include/linux/timer.h | 2 +- kernel/time/timer.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/linux/timer.h b/include/linux/timer.h index 1e6650ed066d..0dc19a8c39c9 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -164,7 +164,7 @@ static inline void destroy_timer_on_stack(struct timer_list *timer) { } */ static inline int timer_pending(const struct timer_list * timer) { - return timer->entry.pprev != NULL; + return !hlist_unhashed_lockless(&timer->entry); } extern void add_timer_on(struct timer_list *timer, int cpu); diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 4820823515e9..568564ae3597 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -944,6 +944,7 @@ static struct timer_base *lock_timer_base(struct timer_list *timer, #define MOD_TIMER_PENDING_ONLY 0x01 #define MOD_TIMER_REDUCE 0x02 +#define MOD_TIMER_NOTPENDING 0x04 static inline int __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int options) @@ -960,7 +961,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option * the timer is re-modified to have the same timeout or ends up in the * same array bucket then just return: */ - if (timer_pending(timer)) { + if (!(options & MOD_TIMER_NOTPENDING) && timer_pending(timer)) { /* * The downside of this optimization is that it can result in * larger granularity than you would get from adding a new @@ -1133,7 +1134,7 @@ EXPORT_SYMBOL(timer_reduce); void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); - mod_timer(timer, timer->expires); + __mod_timer(timer, timer->expires, MOD_TIMER_NOTPENDING); } EXPORT_SYMBOL(add_timer); @@ -1891,7 +1892,7 @@ signed long __sched schedule_timeout(signed long timeout) timer.task = current; timer_setup_on_stack(&timer.timer, process_timeout, 0); - __mod_timer(&timer.timer, expire, 0); + __mod_timer(&timer.timer, expire, MOD_TIMER_NOTPENDING); schedule(); del_singleshot_timer_sync(&timer.timer); From 57721fd15a02f7df9dad1f3cca27f21e03ee118f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 15 Jan 2020 19:17:02 -0800 Subject: [PATCH 0382/2231] rcu: Remove dead code from rcu_segcblist_insert_pend_cbs() The rcu_segcblist_insert_pend_cbs() function currently (partially) initializes the rcu_cblist that it pulls callbacks from. However, all the resulting stores are dead because all callers pass in the address of an on-stack cblist that is not used afterwards. This commit therefore removes this pointless initialization. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcu_segcblist.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/rcu/rcu_segcblist.c b/kernel/rcu/rcu_segcblist.c index 426a472e7308..9a0f66133b4b 100644 --- a/kernel/rcu/rcu_segcblist.c +++ b/kernel/rcu/rcu_segcblist.c @@ -381,8 +381,6 @@ void rcu_segcblist_insert_pend_cbs(struct rcu_segcblist *rsclp, return; /* Nothing to do. */ WRITE_ONCE(*rsclp->tails[RCU_NEXT_TAIL], rclp->head); WRITE_ONCE(rsclp->tails[RCU_NEXT_TAIL], rclp->tail); - rclp->head = NULL; - rclp->tail = &rclp->head; } /* From 59881bcd85a06565c53fd13ce3b0ad7fa55e560c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 20 Jan 2020 15:29:04 -0800 Subject: [PATCH 0383/2231] rcu: Add WRITE_ONCE() to rcu_state ->gp_start The rcu_state structure's ->gp_start field is read locklessly, so this commit adds the WRITE_ONCE() to an update in order to provide proper documentation and READ_ONCE()/WRITE_ONCE() pairing. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_stall.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index bca637b274fb..488b71d03030 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h @@ -102,7 +102,7 @@ static void record_gp_stall_check_time(void) unsigned long j = jiffies; unsigned long j1; - rcu_state.gp_start = j; + WRITE_ONCE(rcu_state.gp_start, j); j1 = rcu_jiffies_till_stall_check(); /* Record ->gp_start before ->jiffies_stall. */ smp_store_release(&rcu_state.jiffies_stall, j + j1); /* ^^^ */ From aa24f93753e256c4b14fe46f7261f150cff2a50c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 20 Jan 2020 15:43:45 -0800 Subject: [PATCH 0384/2231] rcu: Fix rcu_barrier_callback() race condition The rcu_barrier_callback() function does an atomic_dec_and_test(), and if it is the last CPU to check in, does the required wakeup. Either way, it does an event trace. Unfortunately, this is susceptible to the following sequence of events: o CPU 0 invokes rcu_barrier_callback(), but atomic_dec_and_test() says that it is not last. But at this point, CPU 0 is delayed, perhaps due to an NMI, SMI, or vCPU preemption. o CPU 1 invokes rcu_barrier_callback(), and atomic_dec_and_test() says that it is last. So CPU 1 traces completion and does the needed wakeup. o The awakened rcu_barrier() function does cleanup and releases rcu_state.barrier_mutex. o Another CPU now acquires rcu_state.barrier_mutex and starts another round of rcu_barrier() processing, including updating rcu_state.barrier_sequence. o CPU 0 gets its act back together and does its tracing. Except that rcu_state.barrier_sequence has already been updated, so its tracing is incorrect and probably quite confusing. (Wait! Why did this CPU check in twice for one rcu_barrier() invocation???) This commit therefore causes rcu_barrier_callback() to take a snapshot of the value of rcu_state.barrier_sequence before invoking atomic_dec_and_test(), thus guaranteeing that the event-trace output is sensible, even if the timing of the event-trace output might still be confusing. (Wait! Why did the old rcu_barrier() complete before all of its CPUs checked in???) But being that this is RCU, only so much confusion can reasonably be eliminated. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely and due to the mild consequences of the failure, namely a confusing event trace. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index be59a5d7299d..62383ce5161f 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -3077,15 +3077,22 @@ static void rcu_barrier_trace(const char *s, int cpu, unsigned long done) /* * RCU callback function for rcu_barrier(). If we are last, wake * up the task executing rcu_barrier(). + * + * Note that the value of rcu_state.barrier_sequence must be captured + * before the atomic_dec_and_test(). Otherwise, if this CPU is not last, + * other CPUs might count the value down to zero before this CPU gets + * around to invoking rcu_barrier_trace(), which might result in bogus + * data from the next instance of rcu_barrier(). */ static void rcu_barrier_callback(struct rcu_head *rhp) { + unsigned long __maybe_unused s = rcu_state.barrier_sequence; + if (atomic_dec_and_test(&rcu_state.barrier_cpu_count)) { - rcu_barrier_trace(TPS("LastCB"), -1, - rcu_state.barrier_sequence); + rcu_barrier_trace(TPS("LastCB"), -1, s); complete(&rcu_state.barrier_completion); } else { - rcu_barrier_trace(TPS("CB"), -1, rcu_state.barrier_sequence); + rcu_barrier_trace(TPS("CB"), -1, s); } } From 4dfd5cd83dc4458049c7f6eb9c4f361acc4239ea Mon Sep 17 00:00:00 2001 From: Amol Grover Date: Sat, 18 Jan 2020 22:24:18 +0530 Subject: [PATCH 0385/2231] rculist: Add brackets around cond argument in __list_check_rcu macro Passing a complex lockdep condition to __list_check_rcu results in false positive lockdep splat due to incorrect expression evaluation. For example, a lockdep check condition `cond1 || cond2` is evaluated as `!cond1 || cond2 && !rcu_read_lock_any_held()` which, according to operator precedence, evaluates to `!cond1 || (cond2 && !rcu_read_lock_any_held())`. This would result in a lockdep splat when cond1 is false and cond2 is true which is logically incorrect. Signed-off-by: Amol Grover Acked-by: Joel Fernandes (Google) Signed-off-by: Paul E. McKenney --- include/linux/rculist.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 9f313e4999fe..8214cdc715f2 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -60,9 +60,9 @@ static inline void INIT_LIST_HEAD_RCU(struct list_head *list) #define __list_check_rcu(dummy, cond, extra...) \ ({ \ check_arg_count_one(extra); \ - RCU_LOCKDEP_WARN(!cond && !rcu_read_lock_any_held(), \ + RCU_LOCKDEP_WARN(!(cond) && !rcu_read_lock_any_held(), \ "RCU-list traversed in non-reader section!"); \ - }) + }) #else #define __list_check_rcu(dummy, cond, extra...) \ ({ check_arg_count_one(extra); }) From 5648d6591230c811972543ff146ce969babdd732 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 21 Jan 2020 12:30:22 -0800 Subject: [PATCH 0386/2231] rcu: Don't flag non-starting GPs before GP kthread is running Currently rcu_check_gp_start_stall() complains if a grace period takes too long to start, where "too long" is roughly one RCU CPU stall-warning interval. This has worked well, but there are some debugging Kconfig options (such as CONFIG_EFI_PGT_DUMP=y) that can make booting take a very long time, so much so that the stall-warning interval has expired before RCU's grace-period kthread has even been spawned. This commit therefore resets the rcu_state.gp_req_activity and rcu_state.gp_activity timestamps just before the grace-period kthread is spawned, and modifies the checks and adds ordering to ensure that if rcu_check_gp_start_stall() sees that the grace-period kthread has been spawned, that it will also see the resets applied to the rcu_state.gp_req_activity and rcu_state.gp_activity timestamps. Reported-by: Qian Cai Signed-off-by: Paul E. McKenney [ paulmck: Fix whitespace issues reported by Qian Cai. ] Tested-by: Qian Cai [ paulmck: Simplify grace-period wakeup check per Steve Rostedt feedback. ] --- kernel/rcu/tree.c | 28 ++++++++++++++++------------ kernel/rcu/tree_stall.h | 7 ++++--- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 62383ce5161f..4a4a975503e5 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1202,7 +1202,7 @@ static bool rcu_start_this_gp(struct rcu_node *rnp_start, struct rcu_data *rdp, trace_rcu_this_gp(rnp, rdp, gp_seq_req, TPS("Startedroot")); WRITE_ONCE(rcu_state.gp_flags, rcu_state.gp_flags | RCU_GP_FLAG_INIT); WRITE_ONCE(rcu_state.gp_req_activity, jiffies); - if (!rcu_state.gp_kthread) { + if (!READ_ONCE(rcu_state.gp_kthread)) { trace_rcu_this_gp(rnp, rdp, gp_seq_req, TPS("NoGPkthread")); goto unlock_out; } @@ -1237,12 +1237,13 @@ static bool rcu_future_gp_cleanup(struct rcu_node *rnp) } /* - * Awaken the grace-period kthread. Don't do a self-awaken (unless in - * an interrupt or softirq handler), and don't bother awakening when there - * is nothing for the grace-period kthread to do (as in several CPUs raced - * to awaken, and we lost), and finally don't try to awaken a kthread that - * has not yet been created. If all those checks are passed, track some - * debug information and awaken. + * Awaken the grace-period kthread. Don't do a self-awaken (unless in an + * interrupt or softirq handler, in which case we just might immediately + * sleep upon return, resulting in a grace-period hang), and don't bother + * awakening when there is nothing for the grace-period kthread to do + * (as in several CPUs raced to awaken, we lost), and finally don't try + * to awaken a kthread that has not yet been created. If all those checks + * are passed, track some debug information and awaken. * * So why do the self-wakeup when in an interrupt or softirq handler * in the grace-period kthread's context? Because the kthread might have @@ -1252,10 +1253,10 @@ static bool rcu_future_gp_cleanup(struct rcu_node *rnp) */ static void rcu_gp_kthread_wake(void) { - if ((current == rcu_state.gp_kthread && - !in_irq() && !in_serving_softirq()) || - !READ_ONCE(rcu_state.gp_flags) || - !rcu_state.gp_kthread) + struct task_struct *t = READ_ONCE(rcu_state.gp_kthread); + + if ((current == t && !in_irq() && !in_serving_softirq()) || + !READ_ONCE(rcu_state.gp_flags) || !t) return; WRITE_ONCE(rcu_state.gp_wake_time, jiffies); WRITE_ONCE(rcu_state.gp_wake_seq, READ_ONCE(rcu_state.gp_seq)); @@ -3554,7 +3555,10 @@ static int __init rcu_spawn_gp_kthread(void) } rnp = rcu_get_root(); raw_spin_lock_irqsave_rcu_node(rnp, flags); - rcu_state.gp_kthread = t; + WRITE_ONCE(rcu_state.gp_activity, jiffies); + WRITE_ONCE(rcu_state.gp_req_activity, jiffies); + // Reset .gp_activity and .gp_req_activity before setting .gp_kthread. + smp_store_release(&rcu_state.gp_kthread, t); /* ^^^ */ raw_spin_unlock_irqrestore_rcu_node(rnp, flags); wake_up_process(t); rcu_spawn_nocb_kthreads(); diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index 488b71d03030..16ad7ad9a185 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h @@ -578,6 +578,7 @@ void show_rcu_gp_kthreads(void) unsigned long jw; struct rcu_data *rdp; struct rcu_node *rnp; + struct task_struct *t = READ_ONCE(rcu_state.gp_kthread); j = jiffies; ja = j - READ_ONCE(rcu_state.gp_activity); @@ -585,8 +586,7 @@ void show_rcu_gp_kthreads(void) jw = j - READ_ONCE(rcu_state.gp_wake_time); pr_info("%s: wait state: %s(%d) ->state: %#lx delta ->gp_activity %lu ->gp_req_activity %lu ->gp_wake_time %lu ->gp_wake_seq %ld ->gp_seq %ld ->gp_seq_needed %ld ->gp_flags %#x\n", rcu_state.name, gp_state_getname(rcu_state.gp_state), - rcu_state.gp_state, - rcu_state.gp_kthread ? rcu_state.gp_kthread->state : 0x1ffffL, + rcu_state.gp_state, t ? t->state : 0x1ffffL, ja, jr, jw, (long)READ_ONCE(rcu_state.gp_wake_seq), (long)READ_ONCE(rcu_state.gp_seq), (long)READ_ONCE(rcu_get_root()->gp_seq_needed), @@ -633,7 +633,8 @@ static void rcu_check_gp_start_stall(struct rcu_node *rnp, struct rcu_data *rdp, if (!IS_ENABLED(CONFIG_PROVE_RCU) || rcu_gp_in_progress() || ULONG_CMP_GE(READ_ONCE(rnp_root->gp_seq), - READ_ONCE(rnp_root->gp_seq_needed))) + READ_ONCE(rnp_root->gp_seq_needed)) || + !smp_load_acquire(&rcu_state.gp_kthread)) // Get stable kthread. return; j = jiffies; /* Expensive access, and in common case don't get here. */ if (time_before(j, READ_ONCE(rcu_state.gp_req_activity) + gpssdelay) || From 9ced454807191e44ef093aeeee68194be9ce3a1a Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Mon, 20 Jan 2020 22:42:15 +0000 Subject: [PATCH 0387/2231] rcu: Add missing annotation for rcu_nocb_bypass_lock() Sparse reports warning at rcu_nocb_bypass_lock() |warning: context imbalance in rcu_nocb_bypass_lock() - wrong count at exit To fix this, this commit adds an __acquires(&rdp->nocb_bypass_lock). Given that rcu_nocb_bypass_lock() does actually call raw_spin_lock() when raw_spin_trylock() fails, this not only fixes the warning but also improves on the readability of the code. Signed-off-by: Jules Irenge Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_plugin.h | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 0f8b714f09f5..6db2cad7dab7 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1486,6 +1486,7 @@ module_param(nocb_nobypass_lim_per_jiffy, int, 0); * flag the contention. */ static void rcu_nocb_bypass_lock(struct rcu_data *rdp) + __acquires(&rdp->nocb_bypass_lock) { lockdep_assert_irqs_disabled(); if (raw_spin_trylock(&rdp->nocb_bypass_lock)) From 92c0b889f2ff6898710d49458b6eae1de50895c6 Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Thu, 30 Jan 2020 00:30:09 +0000 Subject: [PATCH 0388/2231] rcu/nocb: Add missing annotation for rcu_nocb_bypass_unlock() Sparse reports warning at rcu_nocb_bypass_unlock() warning: context imbalance in rcu_nocb_bypass_unlock() - unexpected unlock The root cause is a missing annotation of rcu_nocb_bypass_unlock() which causes the warning. This commit therefore adds the missing __releases(&rdp->nocb_bypass_lock) annotation. Signed-off-by: Jules Irenge Signed-off-by: Paul E. McKenney Acked-by: Boqun Feng --- kernel/rcu/tree_plugin.h | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 6db2cad7dab7..384651915d74 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1530,6 +1530,7 @@ static bool rcu_nocb_bypass_trylock(struct rcu_data *rdp) * Release the specified rcu_data structure's ->nocb_bypass_lock. */ static void rcu_nocb_bypass_unlock(struct rcu_data *rdp) + __releases(&rdp->nocb_bypass_lock) { lockdep_assert_irqs_disabled(); raw_spin_unlock(&rdp->nocb_bypass_lock); From faa059c397dec8a452c79e9dba64419113ea64e2 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 3 Feb 2020 14:20:00 -0800 Subject: [PATCH 0389/2231] rcu: Optimize and protect atomic_cmpxchg() loop This commit reworks the atomic_cmpxchg() loop in rcu_eqs_special_set() to do only the initial read from the current CPU's rcu_data structure's ->dynticks field explicitly. On subsequent passes, this value is instead retained from the failing atomic_cmpxchg() operation. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 4a4a975503e5..6c624814ee2d 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -342,14 +342,17 @@ bool rcu_eqs_special_set(int cpu) { int old; int new; + int new_old; struct rcu_data *rdp = &per_cpu(rcu_data, cpu); + new_old = atomic_read(&rdp->dynticks); do { - old = atomic_read(&rdp->dynticks); + old = new_old; if (old & RCU_DYNTICK_CTRL_CTR) return false; new = old | RCU_DYNTICK_CTRL_MASK; - } while (atomic_cmpxchg(&rdp->dynticks, old, new) != old); + new_old = atomic_cmpxchg(&rdp->dynticks, old, new); + } while (new_old != old); return true; } From 13817dd589f426aee9c36e3670e7cb2a7f067d23 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 4 Feb 2020 08:56:41 -0800 Subject: [PATCH 0390/2231] rcu: Tighten rcu_lockdep_assert_cblist_protected() check The ->nocb_lock lockdep assertion is currently guarded by cpu_online(), which is incorrect for no-CBs CPUs, whose callback lists must be protected by ->nocb_lock regardless of whether or not the corresponding CPU is online. This situation could result in failure to detect bugs resulting from failing to hold ->nocb_lock for offline CPUs. This commit therefore removes the cpu_online() guard. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_plugin.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 384651915d74..70b3c0f4ea37 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1579,8 +1579,7 @@ static void rcu_nocb_unlock_irqrestore(struct rcu_data *rdp, static void rcu_lockdep_assert_cblist_protected(struct rcu_data *rdp) { lockdep_assert_irqs_disabled(); - if (rcu_segcblist_is_offloaded(&rdp->cblist) && - cpu_online(rdp->cpu)) + if (rcu_segcblist_is_offloaded(&rdp->cblist)) lockdep_assert_held(&rdp->nocb_lock); } From 3d05031ae6de6ad084aa41263aed1343065233d5 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 4 Feb 2020 14:55:29 -0800 Subject: [PATCH 0391/2231] rcu: Make nocb_gp_wait() double-check unexpected-callback warning Currently, nocb_gp_wait() unconditionally complains if there is a callback not already associated with a grace period. This assumes that either there was no such callback initially on the one hand, or that the rcu_advance_cbs() function assigned all such callbacks to a grace period on the other. However, in theory there are some situations that would prevent rcu_advance_cbs() from assigning all of the callbacks. This commit therefore checks for unassociated callbacks immediately after rcu_advance_cbs() returns, while the corresponding rcu_node structure's ->lock is still held. If there are unassociated callbacks at that point, the subsequent WARN_ON_ONCE() is disabled. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_plugin.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 70b3c0f4ea37..36e71c99970a 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1931,6 +1931,7 @@ static void nocb_gp_wait(struct rcu_data *my_rdp) struct rcu_data *rdp; struct rcu_node *rnp; unsigned long wait_gp_seq = 0; // Suppress "use uninitialized" warning. + bool wasempty = false; /* * Each pass through the following loop checks for CBs and for the @@ -1970,10 +1971,13 @@ static void nocb_gp_wait(struct rcu_data *my_rdp) rcu_seq_done(&rnp->gp_seq, cur_gp_seq))) { raw_spin_lock_rcu_node(rnp); /* irqs disabled. */ needwake_gp = rcu_advance_cbs(rnp, rdp); + wasempty = rcu_segcblist_restempty(&rdp->cblist, + RCU_NEXT_READY_TAIL); raw_spin_unlock_rcu_node(rnp); /* irqs disabled. */ } // Need to wait on some grace period? - WARN_ON_ONCE(!rcu_segcblist_restempty(&rdp->cblist, + WARN_ON_ONCE(wasempty && + !rcu_segcblist_restempty(&rdp->cblist, RCU_NEXT_READY_TAIL)); if (rcu_segcblist_nextgp(&rdp->cblist, &cur_gp_seq)) { if (!needwait_gp || From 34c881745549e78f31ec65f319457c82aacc53bd Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Mon, 20 Jan 2020 15:42:25 +0100 Subject: [PATCH 0392/2231] rcu: Support kfree_bulk() interface in kfree_rcu() The kfree_rcu() logic can be improved further by using kfree_bulk() interface along with "basic batching support" introduced earlier. The are at least two advantages of using "bulk" interface: - in case of large number of kfree_rcu() requests kfree_bulk() reduces the per-object overhead caused by calling kfree() per-object. - reduces the number of cache-misses due to "pointer chasing" between objects which can be far spread between each other. This approach defines a new kfree_rcu_bulk_data structure that stores pointers in an array with a specific size. Number of entries in that array depends on PAGE_SIZE making kfree_rcu_bulk_data structure to be exactly one page. Since it deals with "block-chain" technique there is an extra need in dynamic allocation when a new block is required. Memory is allocated with GFP_NOWAIT | __GFP_NOWARN flags, i.e. that allows to skip direct reclaim under low memory condition to prevent stalling and fails silently under high memory pressure. The "emergency path" gets maintained when a system is run out of memory. In that case objects are linked into regular list. The "rcuperf" was run to analyze this change in terms of memory consumption and kfree_bulk() throughput. 1) Testing on the Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz, 12xCPUs with following parameters: kfree_loops=200000 kfree_alloc_num=1000 kfree_rcu_test=1 kfree_vary_obj_size=1 dev.2020.01.10a branch Default / CONFIG_SLAB 53607352517 ns, loops: 200000, batches: 1885, memory footprint: 1248MB 53529637912 ns, loops: 200000, batches: 1921, memory footprint: 1193MB 53570175705 ns, loops: 200000, batches: 1929, memory footprint: 1250MB Patch / CONFIG_SLAB 23981587315 ns, loops: 200000, batches: 810, memory footprint: 1219MB 23879375281 ns, loops: 200000, batches: 822, memory footprint: 1190MB 24086841707 ns, loops: 200000, batches: 794, memory footprint: 1380MB Default / CONFIG_SLUB 51291025022 ns, loops: 200000, batches: 1713, memory footprint: 741MB 51278911477 ns, loops: 200000, batches: 1671, memory footprint: 719MB 51256183045 ns, loops: 200000, batches: 1719, memory footprint: 647MB Patch / CONFIG_SLUB 50709919132 ns, loops: 200000, batches: 1618, memory footprint: 456MB 50736297452 ns, loops: 200000, batches: 1633, memory footprint: 507MB 50660403893 ns, loops: 200000, batches: 1628, memory footprint: 429MB in case of CONFIG_SLAB there is double increase in performance and slightly higher memory usage. As for CONFIG_SLUB, the performance figures are better together with lower memory usage. 2) Testing on the HiKey-960, arm64, 8xCPUs with below parameters: CONFIG_SLAB=y kfree_loops=200000 kfree_alloc_num=1000 kfree_rcu_test=1 102898760401 ns, loops: 200000, batches: 5822, memory footprint: 158MB 89947009882 ns, loops: 200000, batches: 6715, memory footprint: 115MB rcuperf shows approximately ~12% better throughput in case of using "bulk" interface. The "drain logic" or its RCU callback does the work faster that leads to better throughput. Signed-off-by: Uladzislau Rezki (Sony) Tested-by: Joel Fernandes (Google) Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 204 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 169 insertions(+), 35 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index d91c9156fab2..51a3aa884a7c 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2689,22 +2689,47 @@ EXPORT_SYMBOL_GPL(call_rcu); #define KFREE_DRAIN_JIFFIES (HZ / 50) #define KFREE_N_BATCHES 2 +/* + * This macro defines how many entries the "records" array + * will contain. It is based on the fact that the size of + * kfree_rcu_bulk_data structure becomes exactly one page. + */ +#define KFREE_BULK_MAX_ENTR ((PAGE_SIZE / sizeof(void *)) - 3) + +/** + * struct kfree_rcu_bulk_data - single block to store kfree_rcu() pointers + * @nr_records: Number of active pointers in the array + * @records: Array of the kfree_rcu() pointers + * @next: Next bulk object in the block chain + * @head_free_debug: For debug, when CONFIG_DEBUG_OBJECTS_RCU_HEAD is set + */ +struct kfree_rcu_bulk_data { + unsigned long nr_records; + void *records[KFREE_BULK_MAX_ENTR]; + struct kfree_rcu_bulk_data *next; + struct rcu_head *head_free_debug; +}; + /** * struct kfree_rcu_cpu_work - single batch of kfree_rcu() requests * @rcu_work: Let queue_rcu_work() invoke workqueue handler after grace period * @head_free: List of kfree_rcu() objects waiting for a grace period + * @bhead_free: Bulk-List of kfree_rcu() objects waiting for a grace period * @krcp: Pointer to @kfree_rcu_cpu structure */ struct kfree_rcu_cpu_work { struct rcu_work rcu_work; struct rcu_head *head_free; + struct kfree_rcu_bulk_data *bhead_free; struct kfree_rcu_cpu *krcp; }; /** * struct kfree_rcu_cpu - batch up kfree_rcu() requests for RCU grace period * @head: List of kfree_rcu() objects not yet waiting for a grace period + * @bhead: Bulk-List of kfree_rcu() objects not yet waiting for a grace period + * @bcached: Keeps at most one object for later reuse when build chain blocks * @krw_arr: Array of batches of kfree_rcu() objects waiting for a grace period * @lock: Synchronize access to this structure * @monitor_work: Promote @head to @head_free after KFREE_DRAIN_JIFFIES @@ -2718,6 +2743,8 @@ struct kfree_rcu_cpu_work { */ struct kfree_rcu_cpu { struct rcu_head *head; + struct kfree_rcu_bulk_data *bhead; + struct kfree_rcu_bulk_data *bcached; struct kfree_rcu_cpu_work krw_arr[KFREE_N_BATCHES]; spinlock_t lock; struct delayed_work monitor_work; @@ -2727,14 +2754,24 @@ struct kfree_rcu_cpu { static DEFINE_PER_CPU(struct kfree_rcu_cpu, krc); +static __always_inline void +debug_rcu_head_unqueue_bulk(struct rcu_head *head) +{ +#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD + for (; head; head = head->next) + debug_rcu_head_unqueue(head); +#endif +} + /* * This function is invoked in workqueue context after a grace period. - * It frees all the objects queued on ->head_free. + * It frees all the objects queued on ->bhead_free or ->head_free. */ static void kfree_rcu_work(struct work_struct *work) { unsigned long flags; struct rcu_head *head, *next; + struct kfree_rcu_bulk_data *bhead, *bnext; struct kfree_rcu_cpu *krcp; struct kfree_rcu_cpu_work *krwp; @@ -2744,22 +2781,41 @@ static void kfree_rcu_work(struct work_struct *work) spin_lock_irqsave(&krcp->lock, flags); head = krwp->head_free; krwp->head_free = NULL; + bhead = krwp->bhead_free; + krwp->bhead_free = NULL; spin_unlock_irqrestore(&krcp->lock, flags); - // List "head" is now private, so traverse locklessly. + /* "bhead" is now private, so traverse locklessly. */ + for (; bhead; bhead = bnext) { + bnext = bhead->next; + + debug_rcu_head_unqueue_bulk(bhead->head_free_debug); + + rcu_lock_acquire(&rcu_callback_map); + kfree_bulk(bhead->nr_records, bhead->records); + rcu_lock_release(&rcu_callback_map); + + if (cmpxchg(&krcp->bcached, NULL, bhead)) + free_page((unsigned long) bhead); + + cond_resched_tasks_rcu_qs(); + } + + /* + * Emergency case only. It can happen under low memory + * condition when an allocation gets failed, so the "bulk" + * path can not be temporary maintained. + */ for (; head; head = next) { unsigned long offset = (unsigned long)head->func; next = head->next; - // Potentially optimize with kfree_bulk in future. debug_rcu_head_unqueue(head); rcu_lock_acquire(&rcu_callback_map); trace_rcu_invoke_kfree_callback(rcu_state.name, head, offset); - if (!WARN_ON_ONCE(!__is_kfree_rcu_offset(offset))) { - /* Could be optimized with kfree_bulk() in future. */ + if (!WARN_ON_ONCE(!__is_kfree_rcu_offset(offset))) kfree((void *)head - offset); - } rcu_lock_release(&rcu_callback_map); cond_resched_tasks_rcu_qs(); @@ -2774,26 +2830,48 @@ static void kfree_rcu_work(struct work_struct *work) */ static inline bool queue_kfree_rcu_work(struct kfree_rcu_cpu *krcp) { + struct kfree_rcu_cpu_work *krwp; + bool queued = false; int i; - struct kfree_rcu_cpu_work *krwp = NULL; lockdep_assert_held(&krcp->lock); - for (i = 0; i < KFREE_N_BATCHES; i++) - if (!krcp->krw_arr[i].head_free) { - krwp = &(krcp->krw_arr[i]); - break; + + for (i = 0; i < KFREE_N_BATCHES; i++) { + krwp = &(krcp->krw_arr[i]); + + /* + * Try to detach bhead or head and attach it over any + * available corresponding free channel. It can be that + * a previous RCU batch is in progress, it means that + * immediately to queue another one is not possible so + * return false to tell caller to retry. + */ + if ((krcp->bhead && !krwp->bhead_free) || + (krcp->head && !krwp->head_free)) { + /* Channel 1. */ + if (!krwp->bhead_free) { + krwp->bhead_free = krcp->bhead; + krcp->bhead = NULL; + } + + /* Channel 2. */ + if (!krwp->head_free) { + krwp->head_free = krcp->head; + krcp->head = NULL; + } + + /* + * One work is per one batch, so there are two "free channels", + * "bhead_free" and "head_free" the batch can handle. It can be + * that the work is in the pending state when two channels have + * been detached following each other, one by one. + */ + queue_rcu_work(system_wq, &krwp->rcu_work); + queued = true; } + } - // If a previous RCU batch is in progress, we cannot immediately - // queue another one, so return false to tell caller to retry. - if (!krwp) - return false; - - krwp->head_free = krcp->head; - krcp->head = NULL; - INIT_RCU_WORK(&krwp->rcu_work, kfree_rcu_work); - queue_rcu_work(system_wq, &krwp->rcu_work); - return true; + return queued; } static inline void kfree_rcu_drain_unlock(struct kfree_rcu_cpu *krcp, @@ -2830,19 +2908,65 @@ static void kfree_rcu_monitor(struct work_struct *work) spin_unlock_irqrestore(&krcp->lock, flags); } +static inline bool +kfree_call_rcu_add_ptr_to_bulk(struct kfree_rcu_cpu *krcp, + struct rcu_head *head, rcu_callback_t func) +{ + struct kfree_rcu_bulk_data *bnode; + + if (unlikely(!krcp->initialized)) + return false; + + lockdep_assert_held(&krcp->lock); + + /* Check if a new block is required. */ + if (!krcp->bhead || + krcp->bhead->nr_records == KFREE_BULK_MAX_ENTR) { + bnode = xchg(&krcp->bcached, NULL); + if (!bnode) { + WARN_ON_ONCE(sizeof(struct kfree_rcu_bulk_data) > PAGE_SIZE); + + bnode = (struct kfree_rcu_bulk_data *) + __get_free_page(GFP_NOWAIT | __GFP_NOWARN); + } + + /* Switch to emergency path. */ + if (unlikely(!bnode)) + return false; + + /* Initialize the new block. */ + bnode->nr_records = 0; + bnode->next = krcp->bhead; + bnode->head_free_debug = NULL; + + /* Attach it to the head. */ + krcp->bhead = bnode; + } + +#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD + head->func = func; + head->next = krcp->bhead->head_free_debug; + krcp->bhead->head_free_debug = head; +#endif + + /* Finally insert. */ + krcp->bhead->records[krcp->bhead->nr_records++] = + (void *) head - (unsigned long) func; + + return true; +} + /* - * Queue a request for lazy invocation of kfree() after a grace period. + * Queue a request for lazy invocation of kfree_bulk()/kfree() after a grace + * period. Please note there are two paths are maintained, one is the main one + * that uses kfree_bulk() interface and second one is emergency one, that is + * used only when the main path can not be maintained temporary, due to memory + * pressure. * * Each kfree_call_rcu() request is added to a batch. The batch will be drained - * every KFREE_DRAIN_JIFFIES number of jiffies. All the objects in the batch - * will be kfree'd in workqueue context. This allows us to: - * - * 1. Batch requests together to reduce the number of grace periods during - * heavy kfree_rcu() load. - * - * 2. It makes it possible to use kfree_bulk() on a large number of - * kfree_rcu() requests thus reducing cache misses and the per-object - * overhead of kfree(). + * every KFREE_DRAIN_JIFFIES number of jiffies. All the objects in the batch will + * be free'd in workqueue context. This allows us to: batch requests together to + * reduce the number of grace periods during heavy kfree_rcu() load. */ void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func) { @@ -2861,9 +2985,16 @@ void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func) __func__, head); goto unlock_return; } - head->func = func; - head->next = krcp->head; - krcp->head = head; + + /* + * Under high memory pressure GFP_NOWAIT can fail, + * in that case the emergency path is maintained. + */ + if (unlikely(!kfree_call_rcu_add_ptr_to_bulk(krcp, head, func))) { + head->func = func; + head->next = krcp->head; + krcp->head = head; + } // Set timer to drain after KFREE_DRAIN_JIFFIES. if (rcu_scheduler_active == RCU_SCHEDULER_RUNNING && @@ -3769,8 +3900,11 @@ static void __init kfree_rcu_batch_init(void) struct kfree_rcu_cpu *krcp = per_cpu_ptr(&krc, cpu); spin_lock_init(&krcp->lock); - for (i = 0; i < KFREE_N_BATCHES; i++) + for (i = 0; i < KFREE_N_BATCHES; i++) { + INIT_RCU_WORK(&krcp->krw_arr[i].rcu_work, kfree_rcu_work); krcp->krw_arr[i].krcp = krcp; + } + INIT_DELAYED_WORK(&krcp->monitor_work, kfree_rcu_monitor); krcp->initialized = true; } From 613707929b304737e6eb841588772f1994f6702b Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Mon, 20 Jan 2020 15:42:26 +0100 Subject: [PATCH 0393/2231] rcu: Add a trace event for kfree_rcu() use of kfree_bulk() The event is given three parameters, first one is the name of RCU flavour, second one is the number of elements in array for free and last one is an address of the array holding pointers to be freed by the kfree_bulk() function. To enable the trace event your kernel has to be build with CONFIG_RCU_TRACE=y, after that it is possible to track the events using ftrace subsystem. Signed-off-by: Uladzislau Rezki (Sony) Signed-off-by: Paul E. McKenney --- include/trace/events/rcu.h | 28 ++++++++++++++++++++++++++++ kernel/rcu/tree.c | 3 +++ 2 files changed, 31 insertions(+) diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 5e49b06e8104..49a49e68b916 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -623,6 +623,34 @@ TRACE_EVENT_RCU(rcu_invoke_kfree_callback, __entry->rcuname, __entry->rhp, __entry->offset) ); +/* + * Tracepoint for the invocation of a single RCU callback of the special + * kfree_bulk() form. The first argument is the RCU flavor, the second + * argument is a number of elements in array to free, the third is an + * address of the array holding nr_records entries. + */ +TRACE_EVENT_RCU(rcu_invoke_kfree_bulk_callback, + + TP_PROTO(const char *rcuname, unsigned long nr_records, void **p), + + TP_ARGS(rcuname, nr_records, p), + + TP_STRUCT__entry( + __field(const char *, rcuname) + __field(unsigned long, nr_records) + __field(void **, p) + ), + + TP_fast_assign( + __entry->rcuname = rcuname; + __entry->nr_records = nr_records; + __entry->p = p; + ), + + TP_printk("%s bulk=0x%p nr_records=%lu", + __entry->rcuname, __entry->p, __entry->nr_records) +); + /* * Tracepoint for exiting rcu_do_batch after RCU callbacks have been * invoked. The first argument is the name of the RCU flavor, diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 51a3aa884a7c..909f97efb1ed 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2792,6 +2792,9 @@ static void kfree_rcu_work(struct work_struct *work) debug_rcu_head_unqueue_bulk(bhead->head_free_debug); rcu_lock_acquire(&rcu_callback_map); + trace_rcu_invoke_kfree_bulk_callback(rcu_state.name, + bhead->nr_records, bhead->records); + kfree_bulk(bhead->nr_records, bhead->records); rcu_lock_release(&rcu_callback_map); From 80c503e0e68fbe271680ab48f0fe29bc034b01b7 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 23 Jan 2020 09:19:01 -0800 Subject: [PATCH 0394/2231] locktorture: Print ratio of acquisitions, not failures The __torture_print_stats() function in locktorture.c carefully initializes local variable "min" to statp[0].n_lock_acquired, but then compares it to statp[i].n_lock_fail. Given that the .n_lock_fail field should normally be zero, and given the initialization, it seems reasonable to display the maximum and minimum number acquisitions instead of miscomputing the maximum and minimum number of failures. This commit therefore switches from failures to acquisitions. And this turns out to be not only a day-zero bug, but entirely my own fault. I hate it when that happens! Fixes: 0af3fe1efa53 ("locktorture: Add a lock-torture kernel module") Reported-by: Will Deacon Signed-off-by: Paul E. McKenney Acked-by: Will Deacon Cc: Davidlohr Bueso Cc: Josh Triplett Cc: Peter Zijlstra --- kernel/locking/locktorture.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c index 99475a66c94f..687c1d83dc20 100644 --- a/kernel/locking/locktorture.c +++ b/kernel/locking/locktorture.c @@ -696,10 +696,10 @@ static void __torture_print_stats(char *page, if (statp[i].n_lock_fail) fail = true; sum += statp[i].n_lock_acquired; - if (max < statp[i].n_lock_fail) - max = statp[i].n_lock_fail; - if (min > statp[i].n_lock_fail) - min = statp[i].n_lock_fail; + if (max < statp[i].n_lock_acquired) + max = statp[i].n_lock_acquired; + if (min > statp[i].n_lock_acquired) + min = statp[i].n_lock_acquired; } page += sprintf(page, "%s: Total: %lld Max/Min: %ld/%ld %s Fail: %d %s\n", From 7aabb6f839622bc96a425d93f3f7373167be1e19 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 23 Jan 2020 12:32:31 -0800 Subject: [PATCH 0395/2231] locktorture: Allow CPU-hotplug to be disabled via --bootargs The bootparam_hotplug_cpu() bash function was checking for CPU-hotplug kernel-boot parameters from --bootargs, but that check was specific to rcutorture ("rcutorture\.onoff_"). This commit therefore makes this check also work for locktorture ("torture\.onoff_"). Note that rcuperf does not do CPU-hotplug operations, so it is not necessary to make a similar change for rcuperf. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/functions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh index c3a49fb4d6f6..12810229fddc 100644 --- a/tools/testing/selftests/rcutorture/bin/functions.sh +++ b/tools/testing/selftests/rcutorture/bin/functions.sh @@ -12,7 +12,7 @@ # Returns 1 if the specified boot-parameter string tells rcutorture to # test CPU-hotplug operations. bootparam_hotplug_cpu () { - echo "$1" | grep -q "rcutorture\.onoff_" + echo "$1" | grep -q "torture\.onoff_" } # checkarg --argname argtype $# arg mustmatch cannotmatch From c0e1472d80784206ead1dd803dd4bc10e282b17f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 24 Jan 2020 12:58:15 -0800 Subject: [PATCH 0396/2231] locktorture: Use private random-number generators Both lock_torture_writer() and lock_torture_reader() use the "static" keyword on their DEFINE_TORTURE_RANDOM(rand) declarations, which means that a single instance of a random-number generator are shared among all the writers and another is shared among all the readers. Unfortunately, this random-number generator was not designed for concurrent access. This commit therefore removes both "static" keywords so that each reader and each writer gets its own random-number generator. Signed-off-by: Paul E. McKenney --- kernel/locking/locktorture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c index 687c1d83dc20..5baf904e8f39 100644 --- a/kernel/locking/locktorture.c +++ b/kernel/locking/locktorture.c @@ -618,7 +618,7 @@ static struct lock_torture_ops percpu_rwsem_lock_ops = { static int lock_torture_writer(void *arg) { struct lock_stress_stats *lwsp = arg; - static DEFINE_TORTURE_RANDOM(rand); + DEFINE_TORTURE_RANDOM(rand); VERBOSE_TOROUT_STRING("lock_torture_writer task started"); set_user_nice(current, MAX_NICE); @@ -655,7 +655,7 @@ static int lock_torture_writer(void *arg) static int lock_torture_reader(void *arg) { struct lock_stress_stats *lrsp = arg; - static DEFINE_TORTURE_RANDOM(rand); + DEFINE_TORTURE_RANDOM(rand); VERBOSE_TOROUT_STRING("lock_torture_reader task started"); set_user_nice(current, MAX_NICE); From 28e09a2e48486ce8ff0a72e21570d59b1243b308 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 30 Jan 2020 20:37:04 -0800 Subject: [PATCH 0397/2231] locktorture: Forgive apparent unfairness if CPU hotplug If CPU hotplug testing is enabled, a lock might appear to be maximally unfair just because one of the CPUs was offline almost all the time. This commit therefore forgives unfairness if CPU hotplug testing was enabled. Signed-off-by: Paul E. McKenney --- kernel/locking/locktorture.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c index 5baf904e8f39..5efbfc68ce99 100644 --- a/kernel/locking/locktorture.c +++ b/kernel/locking/locktorture.c @@ -704,7 +704,8 @@ static void __torture_print_stats(char *page, page += sprintf(page, "%s: Total: %lld Max/Min: %ld/%ld %s Fail: %d %s\n", write ? "Writes" : "Reads ", - sum, max, min, max / 2 > min ? "???" : "", + sum, max, min, + !onoff_interval && max / 2 > min ? "???" : "", fail, fail ? "!!!" : ""); if (fail) atomic_inc(&cxt.n_lock_torture_errors); From b5ea03709d12e98fa341aecfa6940cc9f49e8817 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 9 Dec 2019 15:19:45 -0800 Subject: [PATCH 0398/2231] rcu: Clear ->core_needs_qs at GP end or self-reported QS The rcu_data structure's ->core_needs_qs field does not necessarily get cleared in a timely fashion after the corresponding CPUs' quiescent state has been reported. From a functional viewpoint, no harm done, but this can result in excessive invocation of RCU core processing, as witnessed by the kernel test robot, which saw greatly increased softirq overhead. This commit therefore restores the rcu_report_qs_rdp() function's clearing of this field, but only when running on the corresponding CPU. Cases where some other CPU reports the quiescent state (for example, on behalf of an idle CPU) are handled by setting this field appropriately within the __note_gp_changes() function's end-of-grace-period checks. This handling is carried out regardless of whether the end of a grace period actually happened, thus handling the case where a CPU goes non-idle after a quiescent state is reported on its behalf, but before the grace period ends. This fix also avoids cross-CPU updates to ->core_needs_qs, While in the area, this commit changes the __note_gp_changes() need_gp variable's name to need_qs because it is a quiescent state that is needed from the CPU in question. Fixes: ed93dfc6bc00 ("rcu: Confine ->core_needs_qs accesses to the corresponding CPU") Reported-by: kernel test robot Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index d91c9156fab2..31d01f80a1f6 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -1386,7 +1386,7 @@ static void __maybe_unused rcu_advance_cbs_nowake(struct rcu_node *rnp, static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp) { bool ret = false; - bool need_gp; + bool need_qs; const bool offloaded = IS_ENABLED(CONFIG_RCU_NOCB_CPU) && rcu_segcblist_is_offloaded(&rdp->cblist); @@ -1400,10 +1400,13 @@ static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp) unlikely(READ_ONCE(rdp->gpwrap))) { if (!offloaded) ret = rcu_advance_cbs(rnp, rdp); /* Advance CBs. */ + rdp->core_needs_qs = false; trace_rcu_grace_period(rcu_state.name, rdp->gp_seq, TPS("cpuend")); } else { if (!offloaded) ret = rcu_accelerate_cbs(rnp, rdp); /* Recent CBs. */ + if (rdp->core_needs_qs) + rdp->core_needs_qs = !!(rnp->qsmask & rdp->grpmask); } /* Now handle the beginnings of any new-to-this-CPU grace periods. */ @@ -1415,9 +1418,9 @@ static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp) * go looking for one. */ trace_rcu_grace_period(rcu_state.name, rnp->gp_seq, TPS("cpustart")); - need_gp = !!(rnp->qsmask & rdp->grpmask); - rdp->cpu_no_qs.b.norm = need_gp; - rdp->core_needs_qs = need_gp; + need_qs = !!(rnp->qsmask & rdp->grpmask); + rdp->cpu_no_qs.b.norm = need_qs; + rdp->core_needs_qs = need_qs; zero_cpu_stall_ticks(rdp); } rdp->gp_seq = rnp->gp_seq; /* Remember new grace-period state. */ @@ -1987,6 +1990,8 @@ rcu_report_qs_rdp(int cpu, struct rcu_data *rdp) return; } mask = rdp->grpmask; + if (rdp->cpu == smp_processor_id()) + rdp->core_needs_qs = false; if ((rnp->qsmask & mask) == 0) { raw_spin_unlock_irqrestore_rcu_node(rnp, flags); } else { From b2b00ddf193bf83dc561d965c67b18eb54ebcd83 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 30 Oct 2019 11:56:10 -0700 Subject: [PATCH 0399/2231] rcu: React to callback overload by aggressively seeking quiescent states In default configutions, RCU currently waits at least 100 milliseconds before asking cond_resched() and/or resched_rcu() for help seeking quiescent states to end a grace period. But 100 milliseconds can be one good long time during an RCU callback flood, for example, as can happen when user processes repeatedly open and close files in a tight loop. These 100-millisecond gaps in successive grace periods during a callback flood can result in excessive numbers of callbacks piling up, unnecessarily increasing memory footprint. This commit therefore asks cond_resched() and/or resched_rcu() for help as early as the first FQS scan when at least one of the CPUs has more than 20,000 callbacks queued, a number that can be changed using the new rcutree.qovld kernel boot parameter. An auxiliary qovld_calc variable is used to avoid acquisition of locks that have not yet been initialized. Early tests indicate that this reduces the RCU-callback memory footprint during rcutorture floods by from 50% to 4x, depending on configuration. Reported-by: Joel Fernandes (Google) Reported-by: Tejun Heo [ paulmck: Fix bug located by Qian Cai. ] Signed-off-by: Paul E. McKenney Tested-by: Dexuan Cui Tested-by: Qian Cai --- .../admin-guide/kernel-parameters.txt | 9 +++ kernel/rcu/tree.c | 75 ++++++++++++++++++- kernel/rcu/tree.h | 4 + kernel/rcu/tree_plugin.h | 2 + 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index dbc22d684627..dbd4b4a65209 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3980,6 +3980,15 @@ Set threshold of queued RCU callbacks below which batch limiting is re-enabled. + rcutree.qovld= [KNL] + Set threshold of queued RCU callbacks beyond which + RCU's force-quiescent-state scan will aggressively + enlist help from cond_resched() and sched IPIs to + help CPUs more quickly reach quiescent states. + Set to less than zero to make this be set based + on rcutree.qhimark at boot time and to zero to + disable more aggressive help enlistment. + rcutree.rcu_idle_gp_delay= [KNL] Set wakeup interval for idle CPUs that have RCU callbacks (RCU_FAST_NO_HZ=y). diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 31d01f80a1f6..48fba2257748 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -150,6 +150,7 @@ static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) static void invoke_rcu_core(void); static void rcu_report_exp_rdp(struct rcu_data *rdp); static void sync_sched_exp_online_cleanup(int cpu); +static void check_cb_ovld_locked(struct rcu_data *rdp, struct rcu_node *rnp); /* rcuc/rcub kthread realtime priority */ static int kthread_prio = IS_ENABLED(CONFIG_RCU_BOOST) ? 1 : 0; @@ -410,10 +411,15 @@ static long blimit = DEFAULT_RCU_BLIMIT; static long qhimark = DEFAULT_RCU_QHIMARK; #define DEFAULT_RCU_QLOMARK 100 /* Once only this many pending, use blimit. */ static long qlowmark = DEFAULT_RCU_QLOMARK; +#define DEFAULT_RCU_QOVLD_MULT 2 +#define DEFAULT_RCU_QOVLD (DEFAULT_RCU_QOVLD_MULT * DEFAULT_RCU_QHIMARK) +static long qovld = DEFAULT_RCU_QOVLD; /* If this many pending, hammer QS. */ +static long qovld_calc = -1; /* No pre-initialization lock acquisitions! */ module_param(blimit, long, 0444); module_param(qhimark, long, 0444); module_param(qlowmark, long, 0444); +module_param(qovld, long, 0444); static ulong jiffies_till_first_fqs = ULONG_MAX; static ulong jiffies_till_next_fqs = ULONG_MAX; @@ -1072,7 +1078,8 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp) rnhqp = &per_cpu(rcu_data.rcu_need_heavy_qs, rdp->cpu); if (!READ_ONCE(*rnhqp) && (time_after(jiffies, rcu_state.gp_start + jtsq * 2) || - time_after(jiffies, rcu_state.jiffies_resched))) { + time_after(jiffies, rcu_state.jiffies_resched) || + rcu_state.cbovld)) { WRITE_ONCE(*rnhqp, true); /* Store rcu_need_heavy_qs before rcu_urgent_qs. */ smp_store_release(ruqp, true); @@ -1089,8 +1096,8 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp) * So hit them over the head with the resched_cpu() hammer! */ if (tick_nohz_full_cpu(rdp->cpu) && - time_after(jiffies, - READ_ONCE(rdp->last_fqs_resched) + jtsq * 3)) { + (time_after(jiffies, READ_ONCE(rdp->last_fqs_resched) + jtsq * 3) || + rcu_state.cbovld)) { WRITE_ONCE(*ruqp, true); resched_cpu(rdp->cpu); WRITE_ONCE(rdp->last_fqs_resched, jiffies); @@ -1704,8 +1711,9 @@ static void rcu_gp_fqs_loop(void) */ static void rcu_gp_cleanup(void) { - unsigned long gp_duration; + int cpu; bool needgp = false; + unsigned long gp_duration; unsigned long new_gp_seq; bool offloaded; struct rcu_data *rdp; @@ -1751,6 +1759,12 @@ static void rcu_gp_cleanup(void) needgp = __note_gp_changes(rnp, rdp) || needgp; /* smp_mb() provided by prior unlock-lock pair. */ needgp = rcu_future_gp_cleanup(rnp) || needgp; + // Reset overload indication for CPUs no longer overloaded + if (rcu_is_leaf_node(rnp)) + for_each_leaf_node_cpu_mask(rnp, cpu, rnp->cbovldmask) { + rdp = per_cpu_ptr(&rcu_data, cpu); + check_cb_ovld_locked(rdp, rnp); + } sq = rcu_nocb_gp_get(rnp); raw_spin_unlock_irq_rcu_node(rnp); rcu_nocb_gp_cleanup(sq); @@ -2299,10 +2313,13 @@ static void force_qs_rnp(int (*f)(struct rcu_data *rdp)) struct rcu_data *rdp; struct rcu_node *rnp; + rcu_state.cbovld = rcu_state.cbovldnext; + rcu_state.cbovldnext = false; rcu_for_each_leaf_node(rnp) { cond_resched_tasks_rcu_qs(); mask = 0; raw_spin_lock_irqsave_rcu_node(rnp, flags); + rcu_state.cbovldnext |= !!rnp->cbovldmask; if (rnp->qsmask == 0) { if (!IS_ENABLED(CONFIG_PREEMPT_RCU) || rcu_preempt_blocked_readers_cgp(rnp)) { @@ -2583,6 +2600,48 @@ static void rcu_leak_callback(struct rcu_head *rhp) { } +/* + * Check and if necessary update the leaf rcu_node structure's + * ->cbovldmask bit corresponding to the current CPU based on that CPU's + * number of queued RCU callbacks. The caller must hold the leaf rcu_node + * structure's ->lock. + */ +static void check_cb_ovld_locked(struct rcu_data *rdp, struct rcu_node *rnp) +{ + raw_lockdep_assert_held_rcu_node(rnp); + if (qovld_calc <= 0) + return; // Early boot and wildcard value set. + if (rcu_segcblist_n_cbs(&rdp->cblist) >= qovld_calc) + WRITE_ONCE(rnp->cbovldmask, rnp->cbovldmask | rdp->grpmask); + else + WRITE_ONCE(rnp->cbovldmask, rnp->cbovldmask & ~rdp->grpmask); +} + +/* + * Check and if necessary update the leaf rcu_node structure's + * ->cbovldmask bit corresponding to the current CPU based on that CPU's + * number of queued RCU callbacks. No locks need be held, but the + * caller must have disabled interrupts. + * + * Note that this function ignores the possibility that there are a lot + * of callbacks all of which have already seen the end of their respective + * grace periods. This omission is due to the need for no-CBs CPUs to + * be holding ->nocb_lock to do this check, which is too heavy for a + * common-case operation. + */ +static void check_cb_ovld(struct rcu_data *rdp) +{ + struct rcu_node *const rnp = rdp->mynode; + + if (qovld_calc <= 0 || + ((rcu_segcblist_n_cbs(&rdp->cblist) >= qovld_calc) == + !!(READ_ONCE(rnp->cbovldmask) & rdp->grpmask))) + return; // Early boot wildcard value or already set correctly. + raw_spin_lock_rcu_node(rnp); + check_cb_ovld_locked(rdp, rnp); + raw_spin_unlock_rcu_node(rnp); +} + /* * Helper function for call_rcu() and friends. The cpu argument will * normally be -1, indicating "currently running CPU". It may specify @@ -2626,6 +2685,7 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func) rcu_segcblist_init(&rdp->cblist); } + check_cb_ovld(rdp); if (rcu_nocb_try_bypass(rdp, head, &was_alldone, flags)) return; // Enqueued onto ->nocb_bypass, so just leave. /* If we get here, rcu_nocb_try_bypass() acquired ->nocb_lock. */ @@ -3814,6 +3874,13 @@ void __init rcu_init(void) rcu_par_gp_wq = alloc_workqueue("rcu_par_gp", WQ_MEM_RECLAIM, 0); WARN_ON(!rcu_par_gp_wq); srcu_init(); + + /* Fill in default value for rcutree.qovld boot parameter. */ + /* -After- the rcu_node ->lock fields are initialized! */ + if (qovld < 0) + qovld_calc = DEFAULT_RCU_QOVLD_MULT * qhimark; + else + qovld_calc = qovld; } #include "tree_stall.h" diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 0c87e4c161c2..9dc2ec021da5 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -68,6 +68,8 @@ struct rcu_node { /* Online CPUs for next expedited GP. */ /* Any CPU that has ever been online will */ /* have its bit set. */ + unsigned long cbovldmask; + /* CPUs experiencing callback overload. */ unsigned long ffmask; /* Fully functional CPUs. */ unsigned long grpmask; /* Mask to apply to parent qsmask. */ /* Only one bit will be set in this mask. */ @@ -321,6 +323,8 @@ struct rcu_state { atomic_t expedited_need_qs; /* # CPUs left to check in. */ struct swait_queue_head expedited_wq; /* Wait for check-ins. */ int ncpus_snap; /* # CPUs seen last time. */ + u8 cbovld; /* Callback overload now? */ + u8 cbovldnext; /* ^ ^ next time? */ unsigned long jiffies_force_qs; /* Time at which to invoke */ /* force_quiescent_state(). */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index c6ea81cd4189..0be8fad08daa 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -56,6 +56,8 @@ static void __init rcu_bootup_announce_oddness(void) pr_info("\tBoot-time adjustment of callback high-water mark to %ld.\n", qhimark); if (qlowmark != DEFAULT_RCU_QLOMARK) pr_info("\tBoot-time adjustment of callback low-water mark to %ld.\n", qlowmark); + if (qovld != DEFAULT_RCU_QOVLD) + pr_info("\tBoot-time adjustment of callback overload leval to %ld.\n", qovld); if (jiffies_till_first_fqs != ULONG_MAX) pr_info("\tBoot-time adjustment of first FQS scan delay to %ld jiffies.\n", jiffies_till_first_fqs); if (jiffies_till_next_fqs != ULONG_MAX) From 8c14263d351b4766a040346ee917b8d81583a460 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 7 Nov 2019 01:10:55 -0800 Subject: [PATCH 0400/2231] rcu: React to callback overload by boosting RCU readers RCU priority boosting currently is not applied until the grace period is at least 250 milliseconds old (or the number of milliseconds specified by the CONFIG_RCU_BOOST_DELAY Kconfig option). Although this has worked well, it can result in OOM under conditions of RCU callback flooding. One can argue that the real-time systems using RCU priority boosting should carefully avoid RCU callback flooding, but one can just as well argue that an OOM is a rather obnoxious error message. This commit therefore disables the RCU priority boosting delay when there are excessive numbers of callbacks queued. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 0be8fad08daa..4d4637c361b7 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1079,7 +1079,7 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) (rnp->gp_tasks != NULL && rnp->boost_tasks == NULL && rnp->qsmask == 0 && - ULONG_CMP_GE(jiffies, rnp->boost_time))) { + (ULONG_CMP_GE(jiffies, rnp->boost_time) || rcu_state.cbovld))) { if (rnp->exp_tasks == NULL) rnp->boost_tasks = rnp->gp_tasks; raw_spin_unlock_irqrestore_rcu_node(rnp, flags); From aa96a93ba2bbad5e1b24706daffa14c4f1c50d2a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 12 Dec 2019 17:36:43 +0000 Subject: [PATCH 0401/2231] rcu: Fix spelling mistake "leval" -> "level" This commit fixes a spelling mistake in a pr_info() message. Signed-off-by: Colin Ian King Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 4d4637c361b7..0765784012f8 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -57,7 +57,7 @@ static void __init rcu_bootup_announce_oddness(void) if (qlowmark != DEFAULT_RCU_QLOMARK) pr_info("\tBoot-time adjustment of callback low-water mark to %ld.\n", qlowmark); if (qovld != DEFAULT_RCU_QOVLD) - pr_info("\tBoot-time adjustment of callback overload leval to %ld.\n", qovld); + pr_info("\tBoot-time adjustment of callback overload level to %ld.\n", qovld); if (jiffies_till_first_fqs != ULONG_MAX) pr_info("\tBoot-time adjustment of first FQS scan delay to %ld jiffies.\n", jiffies_till_first_fqs); if (jiffies_till_next_fqs != ULONG_MAX) From b692dc4adfcff5430467bec8efed3c07d1772eaf Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 11 Feb 2020 07:29:02 -0800 Subject: [PATCH 0402/2231] rcu: Update __call_rcu() comments The __call_rcu() function's header comment refers to a cpu argument that no longer exists, and the comment of the return path from rcu_nocb_try_bypass() ignores the non-no-CBs CPU case. This commit therefore update both comments. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 48fba2257748..0a9de9fd43ed 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2642,12 +2642,7 @@ static void check_cb_ovld(struct rcu_data *rdp) raw_spin_unlock_rcu_node(rnp); } -/* - * Helper function for call_rcu() and friends. The cpu argument will - * normally be -1, indicating "currently running CPU". It may specify - * a CPU only if that CPU is a no-CBs CPU. Currently, only rcu_barrier() - * is expected to specify a CPU. - */ +/* Helper function for call_rcu() and friends. */ static void __call_rcu(struct rcu_head *head, rcu_callback_t func) { @@ -2688,7 +2683,7 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func) check_cb_ovld(rdp); if (rcu_nocb_try_bypass(rdp, head, &was_alldone, flags)) return; // Enqueued onto ->nocb_bypass, so just leave. - /* If we get here, rcu_nocb_try_bypass() acquired ->nocb_lock. */ + // If no-CBs CPU gets here, rcu_nocb_try_bypass() acquired ->nocb_lock. rcu_segcblist_enqueue(&rdp->cblist, head); if (__is_kfree_rcu_offset((unsigned long)func)) trace_rcu_kfree_callback(rcu_state.name, head, From fcb7381265e6cceb1d54283878d145db52b9d9d7 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 6 Jan 2020 11:59:58 -0800 Subject: [PATCH 0403/2231] rcu-tasks: *_ONCE() for rcu_tasks_cbs_head The RCU tasks list of callbacks, rcu_tasks_cbs_head, is sampled locklessly by rcu_tasks_kthread() when waiting for work to do. This commit therefore applies READ_ONCE() to that lockless sampling and WRITE_ONCE() to the single potential store outside of rcu_tasks_kthread. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/update.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index 6c4b862f57d6..a27df761b149 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -528,7 +528,7 @@ void call_rcu_tasks(struct rcu_head *rhp, rcu_callback_t func) rhp->func = func; raw_spin_lock_irqsave(&rcu_tasks_cbs_lock, flags); needwake = !rcu_tasks_cbs_head; - *rcu_tasks_cbs_tail = rhp; + WRITE_ONCE(*rcu_tasks_cbs_tail, rhp); rcu_tasks_cbs_tail = &rhp->next; raw_spin_unlock_irqrestore(&rcu_tasks_cbs_lock, flags); /* We can't create the thread unless interrupts are enabled. */ @@ -658,7 +658,7 @@ static int __noreturn rcu_tasks_kthread(void *arg) /* If there were none, wait a bit and start over. */ if (!list) { wait_event_interruptible(rcu_tasks_cbs_wq, - rcu_tasks_cbs_head); + READ_ONCE(rcu_tasks_cbs_head)); if (!rcu_tasks_cbs_head) { WARN_ON(signal_pending(current)); schedule_timeout_interruptible(HZ/10); From e1e9bdc00ade6651c11bf5628ee9d313ee6089ac Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Mon, 20 Jan 2020 22:41:26 +0000 Subject: [PATCH 0404/2231] rcu: Add missing annotation for exit_tasks_rcu_start() Sparse reports a warning at exit_tasks_rcu_start(void) |warning: context imbalance in exit_tasks_rcu_start() - wrong count at exit To fix this, this commit adds an __acquires(&tasks_rcu_exit_srcu). Given that exit_tasks_rcu_start() does actually call __srcu_read_lock(), this not only fixes the warning but also improves on the readability of the code. Signed-off-by: Jules Irenge Signed-off-by: Paul E. McKenney Reviewed-by: Joel Fernandes (Google) --- kernel/rcu/update.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index a27df761b149..a04fe54bc4ab 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -801,7 +801,7 @@ static int __init rcu_spawn_tasks_kthread(void) core_initcall(rcu_spawn_tasks_kthread); /* Do the srcu_read_lock() for the above synchronize_srcu(). */ -void exit_tasks_rcu_start(void) +void exit_tasks_rcu_start(void) __acquires(&tasks_rcu_exit_srcu) { preempt_disable(); current->rcu_tasks_idx = __srcu_read_lock(&tasks_rcu_exit_srcu); From 90ba11ba99e0a4cc75302335d10a225b27a44918 Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Mon, 20 Jan 2020 22:41:54 +0000 Subject: [PATCH 0405/2231] rcu: Add missing annotation for exit_tasks_rcu_finish() Sparse reports a warning at exit_tasks_rcu_finish(void) |warning: context imbalance in exit_tasks_rcu_finish() |- wrong count at exit To fix this, this commit adds a __releases(&tasks_rcu_exit_srcu). Given that exit_tasks_rcu_finish() does actually call __srcu_read_lock(), this not only fixes the warning but also improves on the readability of the code. Signed-off-by: Jules Irenge Signed-off-by: Paul E. McKenney Reviewed-by: Joel Fernandes (Google) --- kernel/rcu/update.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index a04fe54bc4ab..ede656c5e1e9 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -809,7 +809,7 @@ void exit_tasks_rcu_start(void) __acquires(&tasks_rcu_exit_srcu) } /* Do the srcu_read_unlock() for the above synchronize_srcu(). */ -void exit_tasks_rcu_finish(void) +void exit_tasks_rcu_finish(void) __releases(&tasks_rcu_exit_srcu) { preempt_disable(); __srcu_read_unlock(&tasks_rcu_exit_srcu, current->rcu_tasks_idx); From 7ff8b4502bc0f576450d4ecbda97fa30e8002ed1 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 22 Dec 2019 19:32:54 -0800 Subject: [PATCH 0406/2231] srcu: Fix __call_srcu()/process_srcu() datarace The srcu_node structure's ->srcu_gp_seq_needed_exp field is accessed locklessly, so updates must use WRITE_ONCE(). This commit therefore adds the needed WRITE_ONCE() invocations. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/srcutree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index 657e6a7d1c03..b1edac93e403 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -550,7 +550,7 @@ static void srcu_gp_end(struct srcu_struct *ssp) snp->srcu_have_cbs[idx] = gpseq; rcu_seq_set_state(&snp->srcu_have_cbs[idx], 1); if (ULONG_CMP_LT(snp->srcu_gp_seq_needed_exp, gpseq)) - snp->srcu_gp_seq_needed_exp = gpseq; + WRITE_ONCE(snp->srcu_gp_seq_needed_exp, gpseq); mask = snp->srcu_data_have_cbs[idx]; snp->srcu_data_have_cbs[idx] = 0; spin_unlock_irq_rcu_node(snp); @@ -660,7 +660,7 @@ static void srcu_funnel_gp_start(struct srcu_struct *ssp, struct srcu_data *sdp, if (snp == sdp->mynode) snp->srcu_data_have_cbs[idx] |= sdp->grpmask; if (!do_norm && ULONG_CMP_LT(snp->srcu_gp_seq_needed_exp, s)) - snp->srcu_gp_seq_needed_exp = s; + WRITE_ONCE(snp->srcu_gp_seq_needed_exp, s); spin_unlock_irqrestore_rcu_node(snp, flags); } From 8c9e0cb32315835825ea1ad725a858a2d2ce4a8e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 22 Dec 2019 19:36:33 -0800 Subject: [PATCH 0407/2231] srcu: Fix __call_srcu()/srcu_get_delay() datarace The srcu_struct structure's ->srcu_gp_seq_needed_exp field is accessed locklessly, so updates must use WRITE_ONCE(). This commit therefore adds the needed WRITE_ONCE() invocations. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/srcutree.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index b1edac93e403..79848f7d575d 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -534,7 +534,7 @@ static void srcu_gp_end(struct srcu_struct *ssp) rcu_seq_end(&ssp->srcu_gp_seq); gpseq = rcu_seq_current(&ssp->srcu_gp_seq); if (ULONG_CMP_LT(ssp->srcu_gp_seq_needed_exp, gpseq)) - ssp->srcu_gp_seq_needed_exp = gpseq; + WRITE_ONCE(ssp->srcu_gp_seq_needed_exp, gpseq); spin_unlock_irq_rcu_node(ssp); mutex_unlock(&ssp->srcu_gp_mutex); /* A new grace period can start at this point. But only one. */ @@ -614,7 +614,7 @@ static void srcu_funnel_exp_start(struct srcu_struct *ssp, struct srcu_node *snp } spin_lock_irqsave_rcu_node(ssp, flags); if (ULONG_CMP_LT(ssp->srcu_gp_seq_needed_exp, s)) - ssp->srcu_gp_seq_needed_exp = s; + WRITE_ONCE(ssp->srcu_gp_seq_needed_exp, s); spin_unlock_irqrestore_rcu_node(ssp, flags); } @@ -674,7 +674,7 @@ static void srcu_funnel_gp_start(struct srcu_struct *ssp, struct srcu_data *sdp, smp_store_release(&ssp->srcu_gp_seq_needed, s); /*^^^*/ } if (!do_norm && ULONG_CMP_LT(ssp->srcu_gp_seq_needed_exp, s)) - ssp->srcu_gp_seq_needed_exp = s; + WRITE_ONCE(ssp->srcu_gp_seq_needed_exp, s); /* If grace period not already done and none in progress, start it. */ if (!rcu_seq_done(&ssp->srcu_gp_seq, s) && From 39f91504a03a7a2abdb52205106942fa4a548d0d Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 22 Dec 2019 19:39:35 -0800 Subject: [PATCH 0408/2231] srcu: Fix process_srcu()/srcu_batches_completed() datarace The srcu_struct structure's ->srcu_idx field is accessed locklessly, so reads must use READ_ONCE(). This commit therefore adds the needed READ_ONCE() invocation where it was missed. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely. Signed-off-by: Paul E. McKenney --- kernel/rcu/srcutree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index 79848f7d575d..119a37319e67 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -1079,7 +1079,7 @@ EXPORT_SYMBOL_GPL(srcu_barrier); */ unsigned long srcu_batches_completed(struct srcu_struct *ssp) { - return ssp->srcu_idx; + return READ_ONCE(ssp->srcu_idx); } EXPORT_SYMBOL_GPL(srcu_batches_completed); From 710426068dc60f2d2e139478d6185710802cdc0a Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 11:42:05 -0800 Subject: [PATCH 0409/2231] srcu: Hold srcu_struct ->lock when updating ->srcu_gp_seq A read of the srcu_struct structure's ->srcu_gp_seq field should not need READ_ONCE() when that structure's ->lock is held. Except that this lock is not always held when updating this field. This commit therefore acquires the lock around updates and removes a now-unneeded READ_ONCE(). This data race was reported by KCSAN. Signed-off-by: Paul E. McKenney [ paulmck: Switch from READ_ONCE() to lock per Peter Zilstra question. ] Acked-by: Peter Zijlstra (Intel) --- kernel/rcu/srcutree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index 119a37319e67..c19c1df0d198 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -450,7 +450,7 @@ static void srcu_gp_start(struct srcu_struct *ssp) spin_unlock_rcu_node(sdp); /* Interrupts remain disabled. */ smp_mb(); /* Order prior store to ->srcu_gp_seq_needed vs. GP start. */ rcu_seq_start(&ssp->srcu_gp_seq); - state = rcu_seq_state(READ_ONCE(ssp->srcu_gp_seq)); + state = rcu_seq_state(ssp->srcu_gp_seq); WARN_ON_ONCE(state != SRCU_STATE_SCAN1); } @@ -1130,7 +1130,9 @@ static void srcu_advance_state(struct srcu_struct *ssp) return; /* readers present, retry later. */ } srcu_flip(ssp); + spin_lock_irq_rcu_node(ssp); rcu_seq_set_state(&ssp->srcu_gp_seq, SRCU_STATE_SCAN2); + spin_unlock_irq_rcu_node(ssp); } if (rcu_seq_state(READ_ONCE(ssp->srcu_gp_seq)) == SRCU_STATE_SCAN2) { From 59ee0326ccf712f9a637d5df2465a16a784cbfb0 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 28 Nov 2019 18:54:06 -0800 Subject: [PATCH 0410/2231] rcutorture: Suppress forward-progress complaints during early boot Some larger systems can take in excess of 50 seconds to complete their early boot initcalls prior to spawing init. This does not in any way help the forward-progress judgments of built-in rcutorture (when rcutorture is built as a module, the insmod or modprobe command normally cannot happen until some time after boot completes). This commit therefore suppresses such complaints until about the time that init is spawned. This also includes a fix to a stupid error located by kbuild test robot. [ paulmck: Apply kbuild test robot feedback. ] Signed-off-by: Paul E. McKenney [ paulmck: Fix to nohz_full slow-expediting recovery logic, per bpetkov. ] [ paulmck: Restrict splat to CONFIG_PREEMPT_RT=y kernels and simplify. ] Tested-by: Borislav Petkov --- include/linux/rcutiny.h | 1 + include/linux/rcutree.h | 1 + kernel/rcu/rcutorture.c | 3 ++- kernel/rcu/tree_exp.h | 7 ++++++- kernel/rcu/update.c | 12 ++++++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index b2b2dc990da9..045c28b71f4f 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -83,6 +83,7 @@ void rcu_scheduler_starting(void); static inline void rcu_scheduler_starting(void) { } #endif /* #else #ifndef CONFIG_SRCU */ static inline void rcu_end_inkernel_boot(void) { } +static inline bool rcu_inkernel_boot_has_ended(void) { return true; } static inline bool rcu_is_watching(void) { return true; } static inline void rcu_momentary_dyntick_idle(void) { } static inline void kfree_rcu_scheduler_running(void) { } diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 2f787b9029d1..45f3f66bb04d 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -54,6 +54,7 @@ void exit_rcu(void); void rcu_scheduler_starting(void); extern int rcu_scheduler_active __read_mostly; void rcu_end_inkernel_boot(void); +bool rcu_inkernel_boot_has_ended(void); bool rcu_is_watching(void); #ifndef CONFIG_PREEMPTION void rcu_all_qs(void); diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 1aeecc165b21..9ba49788cb48 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1067,7 +1067,8 @@ rcu_torture_writer(void *arg) if (stutter_wait("rcu_torture_writer") && !READ_ONCE(rcu_fwd_cb_nodelay) && !cur_ops->slow_gps && - !torture_must_stop()) + !torture_must_stop() && + rcu_inkernel_boot_has_ended()) for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++) if (list_empty(&rcu_tortures[i].rtort_free) && rcu_access_pointer(rcu_torture_current) != diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index dcbd75791f39..a72d16eb869e 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -485,6 +485,7 @@ static bool synchronize_rcu_expedited_wait_once(long tlimit) static void synchronize_rcu_expedited_wait(void) { int cpu; + unsigned long j; unsigned long jiffies_stall; unsigned long jiffies_start; unsigned long mask; @@ -496,7 +497,7 @@ static void synchronize_rcu_expedited_wait(void) trace_rcu_exp_grace_period(rcu_state.name, rcu_exp_gp_seq_endval(), TPS("startwait")); jiffies_stall = rcu_jiffies_till_stall_check(); jiffies_start = jiffies; - if (IS_ENABLED(CONFIG_NO_HZ_FULL)) { + if (tick_nohz_full_enabled() && rcu_inkernel_boot_has_ended()) { if (synchronize_rcu_expedited_wait_once(1)) return; rcu_for_each_leaf_node(rnp) { @@ -508,6 +509,10 @@ static void synchronize_rcu_expedited_wait(void) tick_dep_set_cpu(cpu, TICK_DEP_BIT_RCU_EXP); } } + j = READ_ONCE(jiffies_till_first_fqs); + if (synchronize_rcu_expedited_wait_once(j + HZ)) + return; + WARN_ON_ONCE(IS_ENABLED(CONFIG_PREEMPT_RT)); } for (;;) { diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index 6c4b862f57d6..feaaec5747a3 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -183,6 +183,8 @@ void rcu_unexpedite_gp(void) } EXPORT_SYMBOL_GPL(rcu_unexpedite_gp); +static bool rcu_boot_ended __read_mostly; + /* * Inform RCU of the end of the in-kernel boot sequence. */ @@ -191,8 +193,18 @@ void rcu_end_inkernel_boot(void) rcu_unexpedite_gp(); if (rcu_normal_after_boot) WRITE_ONCE(rcu_normal, 1); + rcu_boot_ended = 1; } +/* + * Let rcutorture know when it is OK to turn it up to eleven. + */ +bool rcu_inkernel_boot_has_ended(void) +{ + return rcu_boot_ended; +} +EXPORT_SYMBOL_GPL(rcu_inkernel_boot_has_ended); + #endif /* #ifndef CONFIG_TINY_RCU */ /* From 90e23b6b81a9b374d2940cb0b33935d53664509e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 2 Dec 2019 13:24:07 -0800 Subject: [PATCH 0411/2231] torture: Make results-directory date format completion-friendly The names of the per-test results directories are of the form 2019.11.29-20:42:19. This works, but the ":" characters make tab-based shell name completion a bit onerous because the user must remember to include a quote character somewhere before the first ":". This commit therefore changes the ":" characters to periods, as in 2019.12.01-20.48.01", which allows tab-based completion to work more naturally. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/kvm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh index 78d18ab8e954..2315e2ec12d6 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh @@ -39,7 +39,7 @@ TORTURE_TRUST_MAKE="" resdir="" configs="" cpus=0 -ds=`date +%Y.%m.%d-%H:%M:%S` +ds=`date +%Y.%m.%d-%H.%M.%S` jitter="-1" usage () { From 435508095ab5b6870e8140948983920ce4684e9b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 4 Dec 2019 15:58:41 -0800 Subject: [PATCH 0412/2231] rcutorture: Refrain from callback flooding during boot Additional rcutorture aggression can result in, believe it or not, boot times in excess of three minutes on large hyperthreaded systems. This is long enough for rcutorture to decide to do some callback flooding, which seems a bit excessive given that userspace cannot have started until long after boot, and it is userspace that does the real-world callback flooding. Worse yet, because Tiny RCU lacks forward-progress functionality, the looping-in-the-kernel tests can also be problematic during early boot. This commit therefore causes rcutorture to hold off on callback flooding until about the time that init is spawned, and the same for looping-in-the-kernel tests for Tiny RCU. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 9ba49788cb48..08fa4ef23914 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1994,8 +1994,11 @@ static int rcu_torture_fwd_prog(void *args) schedule_timeout_interruptible(fwd_progress_holdoff * HZ); WRITE_ONCE(rcu_fwd_emergency_stop, false); register_oom_notifier(&rcutorture_oom_nb); - rcu_torture_fwd_prog_nr(rfp, &tested, &tested_tries); - rcu_torture_fwd_prog_cr(rfp); + if (!IS_ENABLED(CONFIG_TINY_RCU) || + rcu_inkernel_boot_has_ended()) + rcu_torture_fwd_prog_nr(rfp, &tested, &tested_tries); + if (rcu_inkernel_boot_has_ended()) + rcu_torture_fwd_prog_cr(rfp); unregister_oom_notifier(&rcutorture_oom_nb); /* Avoid slow periods, better to test when busy. */ From a59ee765a6890e7f4281070008a2654337458311 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 5 Dec 2019 10:49:11 -0800 Subject: [PATCH 0413/2231] torture: Forgive -EBUSY from boottime CPU-hotplug operations During boot, CPU hotplug is often disabled, for example by PCI probing. On large systems that take substantial time to boot, this can result in spurious RCU_HOTPLUG errors. This commit therefore forgives any boottime -EBUSY CPU-hotplug failures by adjusting counters to pretend that the corresponding attempt never happened. A non-splat record of the failed attempt is emitted to the console with the added string "(-EBUSY forgiven during boot)". Signed-off-by: Paul E. McKenney --- kernel/torture.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/kernel/torture.c b/kernel/torture.c index 7c13f5558b71..e377b5b17de8 100644 --- a/kernel/torture.c +++ b/kernel/torture.c @@ -84,6 +84,7 @@ bool torture_offline(int cpu, long *n_offl_attempts, long *n_offl_successes, { unsigned long delta; int ret; + char *s; unsigned long starttime; if (!cpu_online(cpu) || !cpu_is_hotpluggable(cpu)) @@ -99,10 +100,16 @@ bool torture_offline(int cpu, long *n_offl_attempts, long *n_offl_successes, (*n_offl_attempts)++; ret = cpu_down(cpu); if (ret) { + s = ""; + if (!rcu_inkernel_boot_has_ended() && ret == -EBUSY) { + // PCI probe frequently disables hotplug during boot. + (*n_offl_attempts)--; + s = " (-EBUSY forgiven during boot)"; + } if (verbose) pr_alert("%s" TORTURE_FLAG - "torture_onoff task: offline %d failed: errno %d\n", - torture_type, cpu, ret); + "torture_onoff task: offline %d failed%s: errno %d\n", + torture_type, cpu, s, ret); } else { if (verbose > 1) pr_alert("%s" TORTURE_FLAG @@ -137,6 +144,7 @@ bool torture_online(int cpu, long *n_onl_attempts, long *n_onl_successes, { unsigned long delta; int ret; + char *s; unsigned long starttime; if (cpu_online(cpu) || !cpu_is_hotpluggable(cpu)) @@ -150,10 +158,16 @@ bool torture_online(int cpu, long *n_onl_attempts, long *n_onl_successes, (*n_onl_attempts)++; ret = cpu_up(cpu); if (ret) { + s = ""; + if (!rcu_inkernel_boot_has_ended() && ret == -EBUSY) { + // PCI probe frequently disables hotplug during boot. + (*n_onl_attempts)--; + s = " (-EBUSY forgiven during boot)"; + } if (verbose) pr_alert("%s" TORTURE_FLAG - "torture_onoff task: online %d failed: errno %d\n", - torture_type, cpu, ret); + "torture_onoff task: online %d failed%s: errno %d\n", + torture_type, cpu, s, ret); } else { if (verbose > 1) pr_alert("%s" TORTURE_FLAG From 58c53360b36d2077cbb843e7ad2bf75f0498271c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 5 Dec 2019 11:29:01 -0800 Subject: [PATCH 0414/2231] rcutorture: Allow boottime stall warnings to be suppressed In normal production, an RCU CPU stall warning at boottime is often just as bad as at any other time. In fact, given the desire for fast boot, any sort of long-term stall at boot is a bad idea. However, heavy rcutorture testing on large hyperthreaded systems can generate boottime RCU CPU stalls as a matter of course. This commit therefore provides a kernel boot parameter that suppresses reporting of boottime RCU CPU stall warnings and similarly of rcutorture writer stalls. Signed-off-by: Paul E. McKenney --- Documentation/admin-guide/kernel-parameters.txt | 6 ++++++ kernel/rcu/rcu.h | 17 +++++++++++++++++ kernel/rcu/rcutorture.c | 2 +- kernel/rcu/tree_exp.h | 2 +- kernel/rcu/tree_stall.h | 6 +++--- kernel/rcu/update.c | 8 +++++++- 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index dbc22d684627..ee007b5c874f 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4195,6 +4195,12 @@ rcupdate.rcu_cpu_stall_suppress= [KNL] Suppress RCU CPU stall warning messages. + rcupdate.rcu_cpu_stall_suppress_at_boot= [KNL] + Suppress RCU CPU stall warning messages and + rcutorture writer stall warnings that occur + during early boot, that is, during the time + before the init task is spawned. + rcupdate.rcu_cpu_stall_timeout= [KNL] Set timeout for RCU CPU stall warning messages. diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h index 05f936ed167a..1779cbf33cd1 100644 --- a/kernel/rcu/rcu.h +++ b/kernel/rcu/rcu.h @@ -198,6 +198,13 @@ static inline void debug_rcu_head_unqueue(struct rcu_head *head) } #endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ +extern int rcu_cpu_stall_suppress_at_boot; + +static inline bool rcu_stall_is_suppressed_at_boot(void) +{ + return rcu_cpu_stall_suppress_at_boot && !rcu_inkernel_boot_has_ended(); +} + #ifdef CONFIG_RCU_STALL_COMMON extern int rcu_cpu_stall_ftrace_dump; @@ -205,6 +212,11 @@ extern int rcu_cpu_stall_suppress; extern int rcu_cpu_stall_timeout; int rcu_jiffies_till_stall_check(void); +static inline bool rcu_stall_is_suppressed(void) +{ + return rcu_stall_is_suppressed_at_boot() || rcu_cpu_stall_suppress; +} + #define rcu_ftrace_dump_stall_suppress() \ do { \ if (!rcu_cpu_stall_suppress) \ @@ -218,6 +230,11 @@ do { \ } while (0) #else /* #endif #ifdef CONFIG_RCU_STALL_COMMON */ + +static inline bool rcu_stall_is_suppressed(void) +{ + return rcu_stall_is_suppressed_at_boot(); +} #define rcu_ftrace_dump_stall_suppress() #define rcu_ftrace_dump_stall_unsuppress() #endif /* #ifdef CONFIG_RCU_STALL_COMMON */ diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 08fa4ef23914..16c84ec182bd 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1479,7 +1479,7 @@ rcu_torture_stats_print(void) if (cur_ops->stats) cur_ops->stats(); if (rtcv_snap == rcu_torture_current_version && - rcu_torture_current != NULL) { + rcu_torture_current != NULL && !rcu_stall_is_suppressed()) { int __maybe_unused flags = 0; unsigned long __maybe_unused gp_seq = 0; diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index a72d16eb869e..c28d9f0034c3 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -518,7 +518,7 @@ static void synchronize_rcu_expedited_wait(void) for (;;) { if (synchronize_rcu_expedited_wait_once(jiffies_stall)) return; - if (rcu_cpu_stall_suppress) + if (rcu_stall_is_suppressed()) continue; panic_on_rcu_stall(); pr_err("INFO: %s detected expedited stalls on CPUs/tasks: {", diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index 55f9b84790d3..7ee8a1cc0d8b 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h @@ -383,7 +383,7 @@ static void print_other_cpu_stall(unsigned long gp_seq) /* Kick and suppress, if so configured. */ rcu_stall_kick_kthreads(); - if (rcu_cpu_stall_suppress) + if (rcu_stall_is_suppressed()) return; /* @@ -452,7 +452,7 @@ static void print_cpu_stall(void) /* Kick and suppress, if so configured. */ rcu_stall_kick_kthreads(); - if (rcu_cpu_stall_suppress) + if (rcu_stall_is_suppressed()) return; /* @@ -504,7 +504,7 @@ static void check_cpu_stall(struct rcu_data *rdp) unsigned long js; struct rcu_node *rnp; - if ((rcu_cpu_stall_suppress && !rcu_kick_kthreads) || + if ((rcu_stall_is_suppressed() && !rcu_kick_kthreads) || !rcu_gp_in_progress()) return; rcu_stall_kick_kthreads(); diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index feaaec5747a3..085f08a898fe 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -476,13 +476,19 @@ EXPORT_SYMBOL_GPL(rcutorture_sched_setaffinity); #ifdef CONFIG_RCU_STALL_COMMON int rcu_cpu_stall_ftrace_dump __read_mostly; module_param(rcu_cpu_stall_ftrace_dump, int, 0644); -int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */ +int rcu_cpu_stall_suppress __read_mostly; // !0 = suppress stall warnings. EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress); module_param(rcu_cpu_stall_suppress, int, 0644); int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT; module_param(rcu_cpu_stall_timeout, int, 0644); #endif /* #ifdef CONFIG_RCU_STALL_COMMON */ +// Suppress boot-time RCU CPU stall warnings and rcutorture writer stall +// warnings. Also used by rcutorture even if stall warnings are excluded. +int rcu_cpu_stall_suppress_at_boot __read_mostly; // !0 = suppress boot stalls. +EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress_at_boot); +module_param(rcu_cpu_stall_suppress_at_boot, int, 0444); + #ifdef CONFIG_TASKS_RCU /* From 4ab00bdd99a906c089b5c20ee7b5cb91e7c61123 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 5 Dec 2019 15:53:28 -0800 Subject: [PATCH 0415/2231] rcutorture: Suppress boottime bad-sequence warnings In normal production, an excessively long wait on a grace period (synchronize_rcu(), for example) at boottime is often just as bad as at any other time. In fact, given the desire for fast boot, any sort of long wait at boot is a bad idea. However, heavy rcutorture testing on large hyperthreaded systems can generate such long waits during boot as a matter of course. This commit therefore causes the rcupdate.rcu_cpu_stall_suppress_at_boot kernel boot parameter to suppress reporting of bootime bad-sequence warning due to excessively long grace-period waits. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 16c84ec182bd..5efd9503df56 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1423,7 +1423,8 @@ rcu_torture_stats_print(void) pr_alert("%s%s ", torture_type, TORTURE_FLAG); pr_cont("rtc: %p %s: %lu tfle: %d rta: %d rtaf: %d rtf: %d ", rcu_torture_current, - rcu_torture_current ? "ver" : "VER", + rcu_torture_current && !rcu_stall_is_suppressed_at_boot() + ? "ver" : "VER", rcu_torture_current_version, list_empty(&rcu_torture_freelist), atomic_read(&n_rcu_torture_alloc), From 8171d3e0dafd37a9c833904e5a936f4154a1e95b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 6 Dec 2019 15:02:59 -0800 Subject: [PATCH 0416/2231] torture: Allow disabling of boottime CPU-hotplug torture operations In theory, RCU-hotplug operations are supposed to work as soon as there is more than one CPU online. However, in practice, in normal production there is no way to make them happen until userspace is up and running. Besides which, on smaller systems, rcutorture doesn't start doing hotplug operations until 30 seconds after the start of boot, which on most systems also means the better part of 30 seconds after the end of boot. This commit therefore provides a new torture.disable_onoff_at_boot kernel boot parameter that suppresses CPU-hotplug torture operations until about the time that init is spawned. Of course, if you know of a need for boottime CPU-hotplug operations, then you should avoid passing this argument to any of the torture tests. You might also want to look at the splats linked to below. Link: https://lore.kernel.org/lkml/20191206185208.GA25636@paulmck-ThinkPad-P72/ Signed-off-by: Paul E. McKenney --- Documentation/admin-guide/kernel-parameters.txt | 4 ++++ kernel/torture.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index ee007b5c874f..868f59a48580 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4873,6 +4873,10 @@ topology updates sent by the hypervisor to this LPAR. + torture.disable_onoff_at_boot= [KNL] + Prevent the CPU-hotplug component of torturing + until after init has spawned. + tp720= [HW,PS2] tpm_suspend_pcr=[HW,TPM] diff --git a/kernel/torture.c b/kernel/torture.c index e377b5b17de8..8683375dc0c7 100644 --- a/kernel/torture.c +++ b/kernel/torture.c @@ -42,6 +42,9 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Paul E. McKenney "); +static bool disable_onoff_at_boot; +module_param(disable_onoff_at_boot, bool, 0444); + static char *torture_type; static int verbose; @@ -229,6 +232,10 @@ torture_onoff(void *arg) VERBOSE_TOROUT_STRING("torture_onoff end holdoff"); } while (!torture_must_stop()) { + if (disable_onoff_at_boot && !rcu_inkernel_boot_has_ended()) { + schedule_timeout_interruptible(HZ / 10); + continue; + } cpu = (torture_random(&rand) >> 4) % (maxcpu + 1); if (!torture_offline(cpu, &n_offline_attempts, &n_offline_successes, From e0714247373b9c0253b002573f63f3e9698b7b30 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 15 Dec 2019 12:11:56 -0800 Subject: [PATCH 0417/2231] rcutorture: Add 100-CPU configuration The small-system rcutorture configurations have served us well for a great many years, but it is now time to add a larger one. This commit does just that, but does not add it to the defaults in CFLIST. This allows the kvm.sh argument '--configs "4*CFLIST TREE10" to run four instances of each of the default configurations concurrently with one instance of the large configuration. Signed-off-by: Paul E. McKenney --- .../selftests/rcutorture/configs/rcu/TREE10 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tools/testing/selftests/rcutorture/configs/rcu/TREE10 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE10 b/tools/testing/selftests/rcutorture/configs/rcu/TREE10 new file mode 100644 index 000000000000..2debe7891aeb --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE10 @@ -0,0 +1,18 @@ +CONFIG_SMP=y +CONFIG_NR_CPUS=100 +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=n +CONFIG_PREEMPT=n +#CHECK#CONFIG_TREE_RCU=y +CONFIG_HZ_PERIODIC=n +CONFIG_NO_HZ_IDLE=y +CONFIG_NO_HZ_FULL=n +CONFIG_RCU_FAST_NO_HZ=n +CONFIG_RCU_TRACE=n +CONFIG_RCU_NOCB_CPU=n +CONFIG_DEBUG_LOCK_ALLOC=n +CONFIG_PROVE_LOCKING=n +#CHECK#CONFIG_PROVE_RCU=n +CONFIG_DEBUG_OBJECTS=n +CONFIG_DEBUG_OBJECTS_RCU_HEAD=n +CONFIG_RCU_EXPERT=n From c0b94ffb66845d13f9ec537a28bd1466b4de2e14 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 16 Dec 2019 12:04:33 -0800 Subject: [PATCH 0418/2231] rcutorture: Summarize summary of build and run results When running the default list of tests, the run summary of a successful (that is, failed to find any errors) run fits easily on a 24-line screen. But a run with something like "--configs '5*CFLIST'" will be 80 lines long, and it is all too easy to miss a failure message when scrolling back. This commit therefore prints out the number of runs with failing builds or runtime failures, but only if there are any such failures. For example, a run with a single build error and a single runtime error would print two lines like this: 1 runs with build errors. 1 runs with runtime errors. Signed-off-by: Paul E. McKenney --- .../selftests/rcutorture/bin/kvm-recheck.sh | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh index e5edd5198725..0326f4a5ff9c 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh @@ -13,6 +13,9 @@ # # Authors: Paul E. McKenney +T=/tmp/kvm-recheck.sh.$$ +trap 'rm -f $T' 0 2 + PATH=`pwd`/tools/testing/selftests/rcutorture/bin:$PATH; export PATH . functions.sh for rd in "$@" @@ -68,4 +71,16 @@ do fi done done -EDITOR=echo kvm-find-errors.sh "${@: -1}" > /dev/null 2>&1 +EDITOR=echo kvm-find-errors.sh "${@: -1}" > $T 2>&1 +ret=$? +builderrors="`tr ' ' '\012' < $T | grep -c '/Make.out.diags'`" +if test "$builderrors" -gt 0 +then + echo $builderrors runs with build errors. +fi +runerrors="`tr ' ' '\012' < $T | grep -c '/console.log.diags'`" +if test "$runerrors" -gt 0 +then + echo $runerrors runs with runtime errors. +fi +exit $ret From beabc806f5aaa158fc90a939215e8b44ee9d7acc Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 16 Dec 2019 12:08:31 -0800 Subject: [PATCH 0419/2231] rcutorture: Make kvm-find-errors.sh abort on bad directory Currently, kvm-find-errors.sh gives a usage prompt when given a bad directory, but then soldiers on, giving a series of confusing error messages. This commit therefore prints an error message and exits when given a bad directory, hopefully reducing confusion. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh b/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh index 1871d00bccd7..6f50722f251f 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh @@ -20,7 +20,9 @@ rundir="${1}" if test -z "$rundir" -o ! -d "$rundir" then + echo Directory "$rundir" not found. echo Usage: $0 directory + exit 1 fi editor=${EDITOR-vi} From 202489101f2e6cee3f6dffc087a4abd5fdfcebda Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 21 Dec 2019 10:41:48 -0800 Subject: [PATCH 0420/2231] rcutorture: Fix rcu_torture_one_read()/rcu_torture_writer() data race The ->rtort_pipe_count field in the rcu_torture structure checks for too-short grace periods, and is therefore read by rcutorture's readers while being updated by rcutorture's writers. This commit therefore adds the needed READ_ONCE() and WRITE_ONCE() invocations. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely and due to this being rcutorture. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 5efd9503df56..edd97465a0f7 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -375,11 +375,12 @@ rcu_torture_pipe_update_one(struct rcu_torture *rp) { int i; - i = rp->rtort_pipe_count; + i = READ_ONCE(rp->rtort_pipe_count); if (i > RCU_TORTURE_PIPE_LEN) i = RCU_TORTURE_PIPE_LEN; atomic_inc(&rcu_torture_wcount[i]); - if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) { + WRITE_ONCE(rp->rtort_pipe_count, i + 1); + if (rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) { rp->rtort_mbtest = 0; return true; } @@ -1015,7 +1016,8 @@ rcu_torture_writer(void *arg) if (i > RCU_TORTURE_PIPE_LEN) i = RCU_TORTURE_PIPE_LEN; atomic_inc(&rcu_torture_wcount[i]); - old_rp->rtort_pipe_count++; + WRITE_ONCE(old_rp->rtort_pipe_count, + old_rp->rtort_pipe_count + 1); switch (synctype[torture_random(&rand) % nsynctypes]) { case RTWS_DEF_FREE: rcu_torture_writer_state = RTWS_DEF_FREE; @@ -1291,7 +1293,7 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp) atomic_inc(&n_rcu_torture_mberror); rtrsp = rcutorture_loop_extend(&readstate, trsp, rtrsp); preempt_disable(); - pipe_count = p->rtort_pipe_count; + pipe_count = READ_ONCE(p->rtort_pipe_count); if (pipe_count > RCU_TORTURE_PIPE_LEN) { /* Should not happen, but... */ pipe_count = RCU_TORTURE_PIPE_LEN; From 102c14d2f87976e8390d2cb892ccd14e3532e020 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 21 Dec 2019 11:23:50 -0800 Subject: [PATCH 0421/2231] rcutorture: Fix stray access to rcu_fwd_cb_nodelay The rcu_fwd_cb_nodelay variable suppresses excessively long read-side delays while carrying out an rcutorture forward-progress test. As such, it is accessed both by readers and updaters, and most of the accesses therefore use *_ONCE(). Except for one in rcu_read_delay(), which this commit fixes. This data race was reported by KCSAN. Not appropriate for backporting due to this being rcutorture. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index edd97465a0f7..124160a610fa 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -339,7 +339,7 @@ rcu_read_delay(struct torture_random_state *rrsp, struct rt_read_seg *rtrsp) * period, and we want a long delay occasionally to trigger * force_quiescent_state. */ - if (!rcu_fwd_cb_nodelay && + if (!READ_ONCE(rcu_fwd_cb_nodelay) && !(torture_random(rrsp) % (nrealreaders * 2000 * longdelay_ms))) { started = cur_ops->get_gp_seq(); ts = rcu_trace_clock_local(); From f042a436c8dc9f9cfe8ed1ee5de372697269657d Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 3 Jan 2020 16:27:00 -0800 Subject: [PATCH 0422/2231] rcutorture: Add READ_ONCE() to rcu_torture_count and rcu_torture_batch The rcutorture rcu_torture_count and rcu_torture_batch per-CPU variables are read locklessly, so this commit adds the READ_ONCE() to a load in order to avoid various types of compiler vandalism^Woptimization. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely and due to this being rcutorture. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 124160a610fa..0b9ce9a00623 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1413,8 +1413,8 @@ rcu_torture_stats_print(void) for_each_possible_cpu(cpu) { for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { - pipesummary[i] += per_cpu(rcu_torture_count, cpu)[i]; - batchsummary[i] += per_cpu(rcu_torture_batch, cpu)[i]; + pipesummary[i] += READ_ONCE(per_cpu(rcu_torture_count, cpu)[i]); + batchsummary[i] += READ_ONCE(per_cpu(rcu_torture_batch, cpu)[i]); } } for (i = RCU_TORTURE_PIPE_LEN - 1; i >= 0; i--) { From 5396d31d3a396039502f75a128bd8064819cba61 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 8 Jan 2020 19:58:13 -0800 Subject: [PATCH 0423/2231] rcutorture: Annotation lockless accesses to rcu_torture_current The rcutorture global variable rcu_torture_current is accessed locklessly, so it must use the RCU pointer load/store primitives. This commit therefore adds several that were missed. This data race was reported by KCSAN. Not appropriate for backporting due to failure being unlikely and due to this being used only by rcutorture. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 0b9ce9a00623..7e01e9a87352 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1407,6 +1407,7 @@ rcu_torture_stats_print(void) int i; long pipesummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; long batchsummary[RCU_TORTURE_PIPE_LEN + 1] = { 0 }; + struct rcu_torture *rtcp; static unsigned long rtcv_snap = ULONG_MAX; static bool splatted; struct task_struct *wtp; @@ -1423,10 +1424,10 @@ rcu_torture_stats_print(void) } pr_alert("%s%s ", torture_type, TORTURE_FLAG); + rtcp = rcu_access_pointer(rcu_torture_current); pr_cont("rtc: %p %s: %lu tfle: %d rta: %d rtaf: %d rtf: %d ", - rcu_torture_current, - rcu_torture_current && !rcu_stall_is_suppressed_at_boot() - ? "ver" : "VER", + rtcp, + rtcp && !rcu_stall_is_suppressed_at_boot() ? "ver" : "VER", rcu_torture_current_version, list_empty(&rcu_torture_freelist), atomic_read(&n_rcu_torture_alloc), @@ -1482,7 +1483,8 @@ rcu_torture_stats_print(void) if (cur_ops->stats) cur_ops->stats(); if (rtcv_snap == rcu_torture_current_version && - rcu_torture_current != NULL && !rcu_stall_is_suppressed()) { + rcu_access_pointer(rcu_torture_current) && + !rcu_stall_is_suppressed()) { int __maybe_unused flags = 0; unsigned long __maybe_unused gp_seq = 0; From 12af660321263d7744d5d06b765c7b03fade3858 Mon Sep 17 00:00:00 2001 From: "Joel Fernandes (Google)" Date: Thu, 19 Dec 2019 11:22:42 -0500 Subject: [PATCH 0424/2231] rcuperf: Measure memory footprint during kfree_rcu() test During changes to kfree_rcu() code, we often check the amount of free memory. As an alternative to checking this manually, this commit adds a measurement in the test itself. It measures four times during the test for available memory, digitally filters these measurements to produce a running average with a weight of 0.5, and compares this digitally filtered value with the amount of available memory at the beginning of the test. Something like the following is printed at the end of the run: Total time taken by all kfree'ers: 6369738407 ns, loops: 10000, batches: 764, memory footprint: 216MB Signed-off-by: Joel Fernandes (Google) Signed-off-by: Paul E. McKenney --- kernel/rcu/rcuperf.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/rcuperf.c b/kernel/rcu/rcuperf.c index da94b89cd531..a4a8d097d84d 100644 --- a/kernel/rcu/rcuperf.c +++ b/kernel/rcu/rcuperf.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -611,6 +612,7 @@ kfree_perf_thread(void *arg) long me = (long)arg; struct kfree_obj *alloc_ptr; u64 start_time, end_time; + long long mem_begin, mem_during = 0; VERBOSE_PERFOUT_STRING("kfree_perf_thread task started"); set_cpus_allowed_ptr(current, cpumask_of(me % nr_cpu_ids)); @@ -626,6 +628,12 @@ kfree_perf_thread(void *arg) } do { + if (!mem_during) { + mem_during = mem_begin = si_mem_available(); + } else if (loop % (kfree_loops / 4) == 0) { + mem_during = (mem_during + si_mem_available()) / 2; + } + for (i = 0; i < kfree_alloc_num; i++) { alloc_ptr = kmalloc(sizeof(struct kfree_obj), GFP_KERNEL); if (!alloc_ptr) @@ -645,9 +653,11 @@ kfree_perf_thread(void *arg) else b_rcu_gp_test_finished = cur_ops->get_gp_seq(); - pr_alert("Total time taken by all kfree'ers: %llu ns, loops: %d, batches: %ld\n", + pr_alert("Total time taken by all kfree'ers: %llu ns, loops: %d, batches: %ld, memory footprint: %lldMB\n", (unsigned long long)(end_time - start_time), kfree_loops, - rcuperf_seq_diff(b_rcu_gp_test_finished, b_rcu_gp_test_started)); + rcuperf_seq_diff(b_rcu_gp_test_finished, b_rcu_gp_test_started), + (mem_begin - mem_during) >> (20 - PAGE_SHIFT)); + if (shutdown) { smp_mb(); /* Assign before wake. */ wake_up(&shutdown_wq); From 50d4b62970e21e9573daf0e3c1138b4d1ebcca47 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 4 Feb 2020 15:00:56 -0800 Subject: [PATCH 0425/2231] rcutorture: Make rcu_torture_barrier_cbs() post from corresponding CPU Currently, rcu_torture_barrier_cbs() posts callbacks from whatever CPU it is running on, which means that all these kthreads might well be posting from the same CPU, which would drastically reduce the effectiveness of this test. This commit therefore uses IPIs to make the callbacks be posted from the corresponding CPU (given by local variable myid). If the IPI fails (which can happen if the target CPU is offline or does not exist at all), the callback is posted on whatever CPU is currently running. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 7e01e9a87352..f82515cded34 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -2053,6 +2053,14 @@ static void rcu_torture_barrier_cbf(struct rcu_head *rcu) atomic_inc(&barrier_cbs_invoked); } +/* IPI handler to get callback posted on desired CPU, if online. */ +static void rcu_torture_barrier1cb(void *rcu_void) +{ + struct rcu_head *rhp = rcu_void; + + cur_ops->call(rhp, rcu_torture_barrier_cbf); +} + /* kthread function to register callbacks used to test RCU barriers. */ static int rcu_torture_barrier_cbs(void *arg) { @@ -2076,9 +2084,11 @@ static int rcu_torture_barrier_cbs(void *arg) * The above smp_load_acquire() ensures barrier_phase load * is ordered before the following ->call(). */ - local_irq_disable(); /* Just to test no-irq call_rcu(). */ - cur_ops->call(&rcu, rcu_torture_barrier_cbf); - local_irq_enable(); + if (smp_call_function_single(myid, rcu_torture_barrier1cb, + &rcu, 1)) { + // IPI failed, so use direct call from current CPU. + cur_ops->call(&rcu, rcu_torture_barrier_cbf); + } if (atomic_dec_and_test(&barrier_cbs_count)) wake_up(&barrier_wq); } while (!torture_must_stop()); From 9470a18fabd056e67ee12059dab04faf6e1f253c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 5 Feb 2020 12:54:34 -0800 Subject: [PATCH 0426/2231] rcutorture: Manually clean up after rcu_barrier() failure Currently, if rcu_barrier() returns too soon, the test waits 100ms and then does another instance of the test. However, if rcu_barrier() were to have waited for more than 100ms too short a time, this could cause the test's rcu_head structures to be reused while they were still on RCU's callback lists. This can result in knock-on errors that obscure the original rcu_barrier() test failure. This commit therefore adds code that attempts to wait until all of the test's callbacks have been invoked. Of course, if RCU completely lost track of the corresponding rcu_head structures, this wait could be forever. This commit therefore also complains if this attempted recovery takes more than one second, and it also gives up when the test ends. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index f82515cded34..5453bd557f43 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -2124,7 +2124,21 @@ static int rcu_torture_barrier(void *arg) pr_err("barrier_cbs_invoked = %d, n_barrier_cbs = %d\n", atomic_read(&barrier_cbs_invoked), n_barrier_cbs); - WARN_ON_ONCE(1); + WARN_ON(1); + // Wait manually for the remaining callbacks + i = 0; + do { + if (WARN_ON(i++ > HZ)) + i = INT_MIN; + schedule_timeout_interruptible(1); + cur_ops->cb_barrier(); + } while (atomic_read(&barrier_cbs_invoked) != + n_barrier_cbs && + !torture_must_stop()); + smp_mb(); // Can't trust ordering if broken. + if (!torture_must_stop()) + pr_err("Recovered: barrier_cbs_invoked = %d\n", + atomic_read(&barrier_cbs_invoked)); } else { n_barrier_successes++; } From a144935ceaed277c3e640b85f4cff89d7cce4b8f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 6 Feb 2020 05:20:18 -0800 Subject: [PATCH 0427/2231] rcutorture: Set KCSAN Kconfig options to detect more data races This commit enables the KCSAN Kconfig options that (1) detect data races between reads and writes even when the writes do not change the variable's value and (2) detect data races involving plain C-language writes. These changes only affect scripted rcutorture runs and can be overridden using the kvm.sh --kconfig argument. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/configs/rcu/CFcommon | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/rcutorture/configs/rcu/CFcommon b/tools/testing/selftests/rcutorture/configs/rcu/CFcommon index e19a444a0684..0e92d85313aa 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/CFcommon +++ b/tools/testing/selftests/rcutorture/configs/rcu/CFcommon @@ -3,3 +3,5 @@ CONFIG_PRINTK_TIME=y CONFIG_HYPERVISOR_GUEST=y CONFIG_PARAVIRT=y CONFIG_KVM_GUEST=y +CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n +CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY=n From 84fb64c28acd85ae4d29b9c81926bdfa5f1bf25e Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Thu, 7 Nov 2019 20:12:15 +1030 Subject: [PATCH 0428/2231] clocksource/drivers/fttmr010: Parametrise shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for supporting the ast2600 which uses a different method to clear bits in the control register, use a callback for performing the shutdown sequence. Reviewed-by: Cédric Le Goater Reviewed-by: Linus Walleij Signed-off-by: Joel Stanley Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191107094218.13210-2-joel@jms.id.au --- drivers/clocksource/timer-fttmr010.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c index fadff7915dd9..c2d30eb9dc72 100644 --- a/drivers/clocksource/timer-fttmr010.c +++ b/drivers/clocksource/timer-fttmr010.c @@ -97,6 +97,7 @@ struct fttmr010 { bool is_aspeed; u32 t1_enable_val; struct clock_event_device clkevt; + int (*timer_shutdown)(struct clock_event_device *evt); #ifdef CONFIG_ARM struct delay_timer delay_timer; #endif @@ -140,9 +141,7 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, u32 cr; /* Stop */ - cr = readl(fttmr010->base + TIMER_CR); - cr &= ~fttmr010->t1_enable_val; - writel(cr, fttmr010->base + TIMER_CR); + fttmr010->timer_shutdown(evt); if (fttmr010->is_aspeed) { /* @@ -183,9 +182,7 @@ static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) u32 cr; /* Stop */ - cr = readl(fttmr010->base + TIMER_CR); - cr &= ~fttmr010->t1_enable_val; - writel(cr, fttmr010->base + TIMER_CR); + fttmr010->timer_shutdown(evt); /* Setup counter start from 0 or ~0 */ writel(0, fttmr010->base + TIMER1_COUNT); @@ -211,9 +208,7 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) u32 cr; /* Stop */ - cr = readl(fttmr010->base + TIMER_CR); - cr &= ~fttmr010->t1_enable_val; - writel(cr, fttmr010->base + TIMER_CR); + fttmr010->timer_shutdown(evt); /* Setup timer to fire at 1/HZ intervals. */ if (fttmr010->is_aspeed) { @@ -350,6 +345,8 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed) fttmr010->tick_rate); } + fttmr010->timer_shutdown = fttmr010_timer_shutdown; + /* * Setup clockevent timer (interrupt-driven) on timer 1. */ @@ -370,10 +367,10 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed) fttmr010->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; fttmr010->clkevt.set_next_event = fttmr010_timer_set_next_event; - fttmr010->clkevt.set_state_shutdown = fttmr010_timer_shutdown; + fttmr010->clkevt.set_state_shutdown = fttmr010->timer_shutdown; fttmr010->clkevt.set_state_periodic = fttmr010_timer_set_periodic; fttmr010->clkevt.set_state_oneshot = fttmr010_timer_set_oneshot; - fttmr010->clkevt.tick_resume = fttmr010_timer_shutdown; + fttmr010->clkevt.tick_resume = fttmr010->timer_shutdown; fttmr010->clkevt.cpumask = cpumask_of(0); fttmr010->clkevt.irq = irq; clockevents_config_and_register(&fttmr010->clkevt, From 5422413ce56877f35415f6e4b53171e6e13ec4c1 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Thu, 7 Nov 2019 20:12:16 +1030 Subject: [PATCH 0429/2231] clocksource/drivers/fttmr010: Set interrupt and shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for supporting the ast2600, pass the shutdown and interrupt functions to the common init callback. Reviewed-by: Cédric Le Goater Reviewed-by: Linus Walleij Signed-off-by: Joel Stanley Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191107094218.13210-3-joel@jms.id.au --- drivers/clocksource/timer-fttmr010.c | 51 +++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c index c2d30eb9dc72..edb1d5f193f5 100644 --- a/drivers/clocksource/timer-fttmr010.c +++ b/drivers/clocksource/timer-fttmr010.c @@ -37,6 +37,11 @@ #define TIMER3_MATCH2 (0x2c) #define TIMER_CR (0x30) +/* + * Control register set to clear for ast2600 only. + */ +#define AST2600_TIMER_CR_CLR (0x3c) + /* * Control register (TMC30) bit fields for fttmr010/gemini/moxart timers. */ @@ -163,6 +168,16 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, return 0; } +static int ast2600_timer_shutdown(struct clock_event_device *evt) +{ + struct fttmr010 *fttmr010 = to_fttmr010(evt); + + /* Stop */ + writel(fttmr010->t1_enable_val, fttmr010->base + AST2600_TIMER_CR_CLR); + + return 0; +} + static int fttmr010_timer_shutdown(struct clock_event_device *evt) { struct fttmr010 *fttmr010 = to_fttmr010(evt); @@ -244,7 +259,21 @@ static irqreturn_t fttmr010_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed) +static irqreturn_t ast2600_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + struct fttmr010 *fttmr010 = to_fttmr010(evt); + + writel(0x1, fttmr010->base + TIMER_INTR_STATE); + + evt->event_handler(evt); + return IRQ_HANDLED; +} + +static int __init fttmr010_common_init(struct device_node *np, + bool is_aspeed, + int (*timer_shutdown)(struct clock_event_device *), + irq_handler_t irq_handler) { struct fttmr010 *fttmr010; int irq; @@ -345,7 +374,7 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed) fttmr010->tick_rate); } - fttmr010->timer_shutdown = fttmr010_timer_shutdown; + fttmr010->timer_shutdown = timer_shutdown; /* * Setup clockevent timer (interrupt-driven) on timer 1. @@ -354,7 +383,7 @@ static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed) writel(0, fttmr010->base + TIMER1_LOAD); writel(0, fttmr010->base + TIMER1_MATCH1); writel(0, fttmr010->base + TIMER1_MATCH2); - ret = request_irq(irq, fttmr010_timer_interrupt, IRQF_TIMER, + ret = request_irq(irq, irq_handler, IRQF_TIMER, "FTTMR010-TIMER1", &fttmr010->clkevt); if (ret) { pr_err("FTTMR010-TIMER1 no IRQ\n"); @@ -401,14 +430,25 @@ out_disable_clock: return ret; } +static __init int ast2600_timer_init(struct device_node *np) +{ + return fttmr010_common_init(np, true, + ast2600_timer_shutdown, + ast2600_timer_interrupt); +} + static __init int aspeed_timer_init(struct device_node *np) { - return fttmr010_common_init(np, true); + return fttmr010_common_init(np, true, + fttmr010_timer_shutdown, + fttmr010_timer_interrupt); } static __init int fttmr010_timer_init(struct device_node *np) { - return fttmr010_common_init(np, false); + return fttmr010_common_init(np, false, + fttmr010_timer_shutdown, + fttmr010_timer_interrupt); } TIMER_OF_DECLARE(fttmr010, "faraday,fttmr010", fttmr010_timer_init); @@ -416,3 +456,4 @@ TIMER_OF_DECLARE(gemini, "cortina,gemini-timer", fttmr010_timer_init); TIMER_OF_DECLARE(moxart, "moxa,moxart-timer", fttmr010_timer_init); TIMER_OF_DECLARE(ast2400, "aspeed,ast2400-timer", aspeed_timer_init); TIMER_OF_DECLARE(ast2500, "aspeed,ast2500-timer", aspeed_timer_init); +TIMER_OF_DECLARE(ast2600, "aspeed,ast2600-timer", ast2600_timer_init); From 5be8badcb64be4c6ac5b0e8b882eca8eb175ec2d Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Thu, 7 Nov 2019 20:12:18 +1030 Subject: [PATCH 0430/2231] dt-bindings: fttmr010: Add ast2600 compatible The ast2600 contains a fttmr010 derivative. Reviewed-by: Linus Walleij Acked-by: Rob Herring Signed-off-by: Joel Stanley Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20191107094218.13210-5-joel@jms.id.au --- Documentation/devicetree/bindings/timer/faraday,fttmr010.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/timer/faraday,fttmr010.txt b/Documentation/devicetree/bindings/timer/faraday,fttmr010.txt index 195792270414..3cb2f4c98d64 100644 --- a/Documentation/devicetree/bindings/timer/faraday,fttmr010.txt +++ b/Documentation/devicetree/bindings/timer/faraday,fttmr010.txt @@ -11,6 +11,7 @@ Required properties: "moxa,moxart-timer", "faraday,fttmr010" "aspeed,ast2400-timer" "aspeed,ast2500-timer" + "aspeed,ast2600-timer" - reg : Should contain registers location and length - interrupts : Should contain the three timer interrupts usually with From 6e256a7932930ec5dedb0eb3a8ef057eaa2b8ee9 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 6 Jan 2020 00:18:19 -0800 Subject: [PATCH 0431/2231] dt-bindings: phy-qcom-qmp: Add SDM845 PCIe to binding Add the compatible and define necessary clocks and resets for the SDM845 GEN2 QMP PCIe phy and GEN3 QHP PCIe phy. Reviewed-by: Rob Herring Reviewed-by: Vinod Koul Signed-off-by: Bjorn Andersson Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt index eac9ad3cbbc8..a214ce6d0db2 100644 --- a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt @@ -12,6 +12,8 @@ Required properties: "qcom,msm8998-qmp-usb3-phy" for USB3 QMP V3 phy on msm8998, "qcom,msm8998-qmp-ufs-phy" for UFS QMP phy on msm8998, "qcom,msm8998-qmp-pcie-phy" for PCIe QMP phy on msm8998, + "qcom,sdm845-qhp-pcie-phy" for QHP PCIe phy on sdm845, + "qcom,sdm845-qmp-pcie-phy" for QMP PCIe phy on sdm845, "qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845, "qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845, "qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845, @@ -52,6 +54,10 @@ Required properties: "ref", "ref_aux". For "qcom,msm8998-qmp-pcie-phy" must contain: "aux", "cfg_ahb", "ref". + For "qcom,sdm845-qhp-pcie-phy" must contain: + "aux", "cfg_ahb", "ref", "refgen". + For "qcom,sdm845-qmp-pcie-phy" must contain: + "aux", "cfg_ahb", "ref", "refgen". For "qcom,sdm845-qmp-usb3-phy" must contain: "aux", "cfg_ahb", "ref", "com_aux". For "qcom,sdm845-qmp-usb3-uni-phy" must contain: @@ -80,6 +86,10 @@ Required properties: "ufsphy". For "qcom,msm8998-qmp-pcie-phy" must contain: "phy", "common". + For "qcom,sdm845-qhp-pcie-phy" must contain: + "phy". + For "qcom,sdm845-qmp-pcie-phy" must contain: + "phy". For "qcom,sdm845-qmp-usb3-phy" must contain: "phy", "common". For "qcom,sdm845-qmp-usb3-uni-phy" must contain: From 421c9a0e97314fc9fc9f94aa47af9ecbd6cb65b6 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 6 Jan 2020 00:18:20 -0800 Subject: [PATCH 0432/2231] phy: qcom: qmp: Add SDM845 PCIe QMP PHY support qcom_qmp_phy_init() is extended to support the additional register writes needed in PCS MISC and the appropriate sequences and resources are defined for the GEN2 PCIe QMP PHY found in SDM845. Tested-by: Vinod Koul Reviewed-by: Vinod Koul Signed-off-by: Bjorn Andersson Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 156 ++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 7db2a94f7a99..42c9301d6064 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -160,6 +160,12 @@ static const unsigned int qmp_v3_usb3phy_regs_layout[] = { [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, }; +static const unsigned int sdm845_qmp_pciephy_regs_layout[] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x174, +}; + static const unsigned int sdm845_ufsphy_regs_layout[] = { [QPHY_START_CTRL] = 0x00, [QPHY_PCS_READY_STATUS] = 0x160, @@ -481,6 +487,109 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = { QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3), }; +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x007), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x71), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_01, 0x59), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xbb), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x0d), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG4, 0x00), +}; + +static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2, 0x52), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2, 0x10), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4, 0x1a), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5, 0x06), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1, 0x00), +}; + static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), @@ -988,6 +1097,8 @@ struct qmp_phy_cfg { int rx_tbl_num; const struct qmp_phy_init_tbl *pcs_tbl; int pcs_tbl_num; + const struct qmp_phy_init_tbl *pcs_misc_tbl; + int pcs_misc_tbl_num; /* clock ids to be requested */ const char * const *clk_list; @@ -1126,6 +1237,10 @@ static const char * const qmp_v3_phy_clk_l[] = { "aux", "cfg_ahb", "ref", "com_aux", }; +static const char * const sdm845_pciephy_clk_l[] = { + "aux", "cfg_ahb", "ref", "refgen", +}; + static const char * const sdm845_ufs_phy_clk_l[] = { "ref", "ref_aux", }; @@ -1139,6 +1254,10 @@ static const char * const msm8996_usb3phy_reset_l[] = { "phy", "common", }; +static const char * const sdm845_pciephy_reset_l[] = { + "phy", +}; + /* list of regulators */ static const char * const qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", @@ -1234,6 +1353,36 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .pwrdn_delay_max = 1005, /* us */ }; +static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 1, + + .serdes_tbl = sdm845_qmp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_serdes_tbl), + .tx_tbl = sdm845_qmp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_tx_tbl), + .rx_tbl = sdm845_qmp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_rx_tbl), + .pcs_tbl = sdm845_qmp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_pcs_tbl), + .pcs_misc_tbl = sdm845_qmp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sdm845_qmp_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sdm845_qmp_pciephy_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -1563,6 +1712,7 @@ static int qcom_qmp_phy_enable(struct phy *phy) void __iomem *tx = qphy->tx; void __iomem *rx = qphy->rx; void __iomem *pcs = qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; void __iomem *dp_com = qmp->dp_com; void __iomem *status; unsigned int mask, val, ready; @@ -1633,6 +1783,9 @@ static int qcom_qmp_phy_enable(struct phy *phy) if (ret) goto err_lane_rst; + qcom_qmp_phy_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl, + cfg->pcs_misc_tbl_num); + /* * Pull out PHY from POWER DOWN state. * This is active low enable signal to power-down PHY. @@ -2102,6 +2255,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,ipq8074-qmp-pcie-phy", .data = &ipq8074_pciephy_cfg, + }, { + .compatible = "qcom,sdm845-qmp-pcie-phy", + .data = &sdm845_qmp_pciephy_cfg, }, { .compatible = "qcom,sdm845-qmp-usb3-phy", .data = &qmp_v3_usb3phy_cfg, From 909a5c78de9126f090eeb44c66accece12b3e689 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 6 Jan 2020 00:18:21 -0800 Subject: [PATCH 0433/2231] phy: qcom: qmp: Add SDM845 QHP PCIe PHY Add the GEN3 QHP PCIe PHY found in SDM845. Tested-by: Julien Massot Tested-by: Vinod Koul Reviewed-by: Vinod Koul Signed-off-by: Bjorn Andersson Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 157 ++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 114 ++++++++++++++++++++ 2 files changed, 271 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 42c9301d6064..9733d75d2597 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -166,6 +166,12 @@ static const unsigned int sdm845_qmp_pciephy_regs_layout[] = { [QPHY_PCS_STATUS] = 0x174, }; +static const unsigned int sdm845_qhp_pciephy_regs_layout[] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_STATUS] = 0x2ac, +}; + static const unsigned int sdm845_ufsphy_regs_layout[] = { [QPHY_START_CTRL] = 0x00, [QPHY_PCS_READY_STATUS] = 0x160, @@ -590,6 +596,126 @@ static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_misc_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1, 0x00), }; +static const struct qmp_phy_init_tbl sdm845_qhp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SYSCLK_EN_SEL, 0x27), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2, 0x07), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1_MODE1, 0x4c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2_MODE1, 0x06), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BIAS_EN_CKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CLK_ENABLE1, 0xb0), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE0, 0x8c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE0, 0x20), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_RESTRIM_CTRL2, 0x05), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE1, 0xaa), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE1, 0x3f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VCO_TUNE_MAP, 0x10), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CLK_SELECT, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_HSCLK_SEL1, 0x30), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORECLK_DIV, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORE_CLK_EN, 0x73), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CMN_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SVS_MODE_CLK_SEL, 0x15), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CMN_MODE, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VREGCLK_DIV1, 0x22), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VREGCLK_DIV2, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BGV_TRIM, 0x20), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BG_CTRL, 0x07), +}; + +static const struct qmp_phy_init_tbl sdm845_qhp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL0, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_TAP_EN, 0x0d), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_TX_BAND_MODE, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_LANE_MODE, 0x1a), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PARALLEL_RATE, 0x2f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE0, 0x09), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE1, 0x09), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE2, 0x1b), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE1, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE2, 0x07), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE0, 0x31), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE1, 0x31), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE2, 0x03), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_THRESH_DFE, 0x02), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CGA_THRESH_DFE, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXENGINE_EN0, 0x12), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_TRAIN_TIME, 0x25), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_DFE_OVRLP_TIME, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_REFRESH_TIME, 0x05), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_ENABLE_TIME, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_VGA_GAIN, 0x26), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_GAIN, 0x12), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EQ_GAIN, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_OFFSET_GAIN, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PRE_GAIN, 0x09), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EQ_INTVAL, 0x15), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EDAC_INITVAL, 0x28), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_INITB0, 0x7f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_INITB1, 0x07), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RCVRDONE_THRESH1, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_CTRL, 0x70), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE0, 0x8b), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE1, 0x08), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE2, 0x0a), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE0, 0x03), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE1, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE2, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_BAND, 0x02), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE0, 0x5c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE1, 0x3e), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE2, 0x3f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_ENABLES, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_CNTRL, 0xa0), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_DEGLITCH_CNTRL, 0x08), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DCC_GAIN, 0x01), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_EN_SIGNAL, 0xc3), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PSM_RX_EN_CAL, 0x00), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_MISC_CNTRL0, 0xbc), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_TS0_TIMER, 0x7f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DLL_HIGHDATARATE, 0x15), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL2, 0x0f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RESETCODE_OFFSET, 0x04), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_VGA_INITVAL, 0x20), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RSM_START, 0x01), +}; + +static const struct qmp_phy_init_tbl sdm845_qhp_pcie_rx_tbl[] = { +}; + +static const struct qmp_phy_init_tbl sdm845_qhp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG, 0x3f), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG, 0x50), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M3P5DB, 0x19), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M3P5DB, 0x07), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M6DB, 0x17), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M6DB, 0x09), + QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5, 0x9f), +}; + static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), @@ -1383,6 +1509,34 @@ static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = { .pwrdn_delay_max = 1005, /* us */ }; +static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 1, + + .serdes_tbl = sdm845_qhp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_serdes_tbl), + .tx_tbl = sdm845_qhp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_tx_tbl), + .rx_tbl = sdm845_qhp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_rx_tbl), + .pcs_tbl = sdm845_qhp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdm845_qhp_pcie_pcs_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sdm845_qhp_pciephy_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -2255,6 +2409,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,ipq8074-qmp-pcie-phy", .data = &ipq8074_pciephy_cfg, + }, { + .compatible = "qcom,sdm845-qhp-pcie-phy", + .data = &sdm845_qhp_pciephy_cfg, }, { .compatible = "qcom,sdm845-qmp-pcie-phy", .data = &sdm845_qmp_pciephy_cfg, diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 90f793c2293d..dece0e67704b 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -409,4 +409,118 @@ #define QPHY_V4_TX_MID_TERM_CTRL1 0x1d8 #define QPHY_V4_MULTI_LANE_CTRL1 0x1e0 +/* PCIE GEN3 COM registers */ +#define PCIE_GEN3_QHP_COM_SSC_EN_CENTER 0x14 +#define PCIE_GEN3_QHP_COM_SSC_PER1 0x20 +#define PCIE_GEN3_QHP_COM_SSC_PER2 0x24 +#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1 0x28 +#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2 0x2c +#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1_MODE1 0x34 +#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2_MODE1 0x38 +#define PCIE_GEN3_QHP_COM_BIAS_EN_CKBUFLR_EN 0x54 +#define PCIE_GEN3_QHP_COM_CLK_ENABLE1 0x58 +#define PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE0 0x6c +#define PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE0 0x70 +#define PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE1 0x78 +#define PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE1 0x7c +#define PCIE_GEN3_QHP_COM_BGV_TRIM 0x98 +#define PCIE_GEN3_QHP_COM_CP_CTRL_MODE0 0xb4 +#define PCIE_GEN3_QHP_COM_CP_CTRL_MODE1 0xb8 +#define PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE0 0xc0 +#define PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE1 0xc4 +#define PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE0 0xcc +#define PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE1 0xd0 +#define PCIE_GEN3_QHP_COM_SYSCLK_EN_SEL 0xdc +#define PCIE_GEN3_QHP_COM_RESTRIM_CTRL2 0xf0 +#define PCIE_GEN3_QHP_COM_LOCK_CMP_EN 0xf8 +#define PCIE_GEN3_QHP_COM_DEC_START_MODE0 0x100 +#define PCIE_GEN3_QHP_COM_DEC_START_MODE1 0x108 +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE0 0x11c +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE0 0x120 +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE0 0x124 +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE1 0x128 +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE1 0x12c +#define PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE1 0x130 +#define PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE0 0x150 +#define PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE1 0x158 +#define PCIE_GEN3_QHP_COM_VCO_TUNE_MAP 0x178 +#define PCIE_GEN3_QHP_COM_BG_CTRL 0x1c8 +#define PCIE_GEN3_QHP_COM_CLK_SELECT 0x1cc +#define PCIE_GEN3_QHP_COM_HSCLK_SEL1 0x1d0 +#define PCIE_GEN3_QHP_COM_CORECLK_DIV 0x1e0 +#define PCIE_GEN3_QHP_COM_CORE_CLK_EN 0x1e8 +#define PCIE_GEN3_QHP_COM_CMN_CONFIG 0x1f0 +#define PCIE_GEN3_QHP_COM_SVS_MODE_CLK_SEL 0x1fc +#define PCIE_GEN3_QHP_COM_CORECLK_DIV_MODE1 0x21c +#define PCIE_GEN3_QHP_COM_CMN_MODE 0x224 +#define PCIE_GEN3_QHP_COM_VREGCLK_DIV1 0x228 +#define PCIE_GEN3_QHP_COM_VREGCLK_DIV2 0x22c + +/* PCIE GEN3 QHP Lane registers */ +#define PCIE_GEN3_QHP_L0_DRVR_CTRL0 0xc +#define PCIE_GEN3_QHP_L0_DRVR_CTRL1 0x10 +#define PCIE_GEN3_QHP_L0_DRVR_CTRL2 0x14 +#define PCIE_GEN3_QHP_L0_DRVR_TAP_EN 0x18 +#define PCIE_GEN3_QHP_L0_TX_BAND_MODE 0x60 +#define PCIE_GEN3_QHP_L0_LANE_MODE 0x64 +#define PCIE_GEN3_QHP_L0_PARALLEL_RATE 0x7c +#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE0 0xc0 +#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE1 0xc4 +#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE2 0xc8 +#define PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE1 0xd0 +#define PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE2 0xd4 +#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE0 0xd8 +#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE1 0xdc +#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE2 0xe0 +#define PCIE_GEN3_QHP_L0_CTLE_THRESH_DFE 0xfc +#define PCIE_GEN3_QHP_L0_CGA_THRESH_DFE 0x100 +#define PCIE_GEN3_QHP_L0_RXENGINE_EN0 0x108 +#define PCIE_GEN3_QHP_L0_CTLE_TRAIN_TIME 0x114 +#define PCIE_GEN3_QHP_L0_CTLE_DFE_OVRLP_TIME 0x118 +#define PCIE_GEN3_QHP_L0_DFE_REFRESH_TIME 0x11c +#define PCIE_GEN3_QHP_L0_DFE_ENABLE_TIME 0x120 +#define PCIE_GEN3_QHP_L0_VGA_GAIN 0x124 +#define PCIE_GEN3_QHP_L0_DFE_GAIN 0x128 +#define PCIE_GEN3_QHP_L0_EQ_GAIN 0x130 +#define PCIE_GEN3_QHP_L0_OFFSET_GAIN 0x134 +#define PCIE_GEN3_QHP_L0_PRE_GAIN 0x138 +#define PCIE_GEN3_QHP_L0_VGA_INITVAL 0x13c +#define PCIE_GEN3_QHP_L0_EQ_INTVAL 0x154 +#define PCIE_GEN3_QHP_L0_EDAC_INITVAL 0x160 +#define PCIE_GEN3_QHP_L0_RXEQ_INITB0 0x168 +#define PCIE_GEN3_QHP_L0_RXEQ_INITB1 0x16c +#define PCIE_GEN3_QHP_L0_RCVRDONE_THRESH1 0x178 +#define PCIE_GEN3_QHP_L0_RXEQ_CTRL 0x180 +#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE0 0x184 +#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE1 0x188 +#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE2 0x18c +#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE0 0x190 +#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE1 0x194 +#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE2 0x198 +#define PCIE_GEN3_QHP_L0_UCDR_SO_CONFIG 0x19c +#define PCIE_GEN3_QHP_L0_RX_BAND 0x1a4 +#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE0 0x1c0 +#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE1 0x1c4 +#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE2 0x1c8 +#define PCIE_GEN3_QHP_L0_SIGDET_ENABLES 0x230 +#define PCIE_GEN3_QHP_L0_SIGDET_CNTRL 0x234 +#define PCIE_GEN3_QHP_L0_SIGDET_DEGLITCH_CNTRL 0x238 +#define PCIE_GEN3_QHP_L0_DCC_GAIN 0x2a4 +#define PCIE_GEN3_QHP_L0_RSM_START 0x2a8 +#define PCIE_GEN3_QHP_L0_RX_EN_SIGNAL 0x2ac +#define PCIE_GEN3_QHP_L0_PSM_RX_EN_CAL 0x2b0 +#define PCIE_GEN3_QHP_L0_RX_MISC_CNTRL0 0x2b8 +#define PCIE_GEN3_QHP_L0_TS0_TIMER 0x2c0 +#define PCIE_GEN3_QHP_L0_DLL_HIGHDATARATE 0x2c4 +#define PCIE_GEN3_QHP_L0_RX_RESETCODE_OFFSET 0x2cc + +/* PCIE GEN3 PCS registers */ +#define PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M3P5DB 0x2c +#define PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M3P5DB 0x40 +#define PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M6DB 0x54 +#define PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M6DB 0x68 +#define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG 0x15c +#define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5 0x16c +#define PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG 0x174 + #endif From 113d4bc9048336ba7c3d2ad972dbad4aef6e148a Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 17 Feb 2020 21:41:53 -0600 Subject: [PATCH 0434/2231] objtool: Fix clang switch table edge case Clang has the ability to create a switch table which is not a jump table, but is rather a table of string pointers. This confuses objtool, because it sees the relocations for the string pointers and assumes they're part of a jump table: drivers/ata/sata_dwc_460ex.o: warning: objtool: sata_dwc_bmdma_start_by_tag()+0x3a2: can't find switch jump table net/ceph/messenger.o: warning: objtool: ceph_con_workfn()+0x47c: can't find switch jump table Make objtool's find_jump_table() smart enough to distinguish between a switch jump table (which has relocations to text addresses in the same function as the original instruction) and other anonymous rodata (which may have relocations to elsewhere). Reported-by: Nick Desaulniers Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Tested-by: Nick Desaulniers Tested-by: Nathan Chancellor Link: https://github.com/ClangBuiltLinux/linux/issues/485 Link: https://lkml.kernel.org/r/263f6aae46d33da0b86d7030ced878cb5cab1788.1581997059.git.jpoimboe@redhat.com --- tools/objtool/check.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index b038de2ccd71..4d6e283951ec 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1025,7 +1025,7 @@ static struct rela *find_jump_table(struct objtool_file *file, struct instruction *insn) { struct rela *text_rela, *table_rela; - struct instruction *orig_insn = insn; + struct instruction *dest_insn, *orig_insn = insn; struct section *table_sec; unsigned long table_offset; @@ -1077,10 +1077,17 @@ static struct rela *find_jump_table(struct objtool_file *file, strcmp(table_sec->name, C_JUMP_TABLE_SECTION)) continue; - /* Each table entry has a rela associated with it. */ + /* + * Each table entry has a rela associated with it. The rela + * should reference text in the same function as the original + * instruction. + */ table_rela = find_rela_by_dest(table_sec, table_offset); if (!table_rela) continue; + dest_insn = find_insn(file, table_rela->sym->sec, table_rela->addend); + if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func) + continue; /* * Use of RIP-relative switch jumps is quite rare, and From 7acfe5315312fc56c2a94c9216448087b38ae909 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 17 Feb 2020 21:41:54 -0600 Subject: [PATCH 0435/2231] objtool: Improve call destination function detection A recent clang change, combined with a binutils bug, can trigger a situation where a ".Lprintk$local" STT_NOTYPE symbol gets created at the same offset as the "printk" STT_FUNC symbol. This confuses objtool: kernel/printk/printk.o: warning: objtool: ignore_loglevel_setup()+0x10: can't find call dest symbol at .text+0xc67 Improve the call destination detection by looking specifically for an STT_FUNC symbol. Reported-by: Nick Desaulniers Signed-off-by: Josh Poimboeuf Signed-off-by: Borislav Petkov Tested-by: Nick Desaulniers Tested-by: Nathan Chancellor Link: https://github.com/ClangBuiltLinux/linux/issues/872 Link: https://sourceware.org/bugzilla/show_bug.cgi?id=25551 Link: https://lkml.kernel.org/r/0a7ee320bc0ea4469bd3dc450a7b4725669e0ea9.1581997059.git.jpoimboe@redhat.com --- tools/objtool/check.c | 27 ++++++++++++++++++--------- tools/objtool/elf.c | 14 ++++++++++++-- tools/objtool/elf.h | 1 + 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4d6e283951ec..6b6178ef4464 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -420,8 +420,8 @@ static void add_ignores(struct objtool_file *file) break; case STT_SECTION: - func = find_symbol_by_offset(rela->sym->sec, rela->addend); - if (!func || func->type != STT_FUNC) + func = find_func_by_offset(rela->sym->sec, rela->addend); + if (!func) continue; break; @@ -665,10 +665,14 @@ static int add_call_destinations(struct objtool_file *file) insn->len); if (!rela) { dest_off = insn->offset + insn->len + insn->immediate; - insn->call_dest = find_symbol_by_offset(insn->sec, - dest_off); + insn->call_dest = find_func_by_offset(insn->sec, dest_off); + if (!insn->call_dest) + insn->call_dest = find_symbol_by_offset(insn->sec, dest_off); - if (!insn->call_dest && !insn->ignore) { + if (insn->ignore) + continue; + + if (!insn->call_dest) { WARN_FUNC("unsupported intra-function call", insn->sec, insn->offset); if (retpoline) @@ -676,11 +680,16 @@ static int add_call_destinations(struct objtool_file *file) return -1; } + if (insn->func && insn->call_dest->type != STT_FUNC) { + WARN_FUNC("unsupported call to non-function", + insn->sec, insn->offset); + return -1; + } + } else if (rela->sym->type == STT_SECTION) { - insn->call_dest = find_symbol_by_offset(rela->sym->sec, - rela->addend+4); - if (!insn->call_dest || - insn->call_dest->type != STT_FUNC) { + insn->call_dest = find_func_by_offset(rela->sym->sec, + rela->addend+4); + if (!insn->call_dest) { WARN_FUNC("can't find call dest symbol at %s+0x%x", insn->sec, insn->offset, rela->sym->sec->name, diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index edba4745f25a..cc4601c879ce 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -62,8 +62,18 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) struct symbol *sym; list_for_each_entry(sym, &sec->symbol_list, list) - if (sym->type != STT_SECTION && - sym->offset == offset) + if (sym->type != STT_SECTION && sym->offset == offset) + return sym; + + return NULL; +} + +struct symbol *find_func_by_offset(struct section *sec, unsigned long offset) +{ + struct symbol *sym; + + list_for_each_entry(sym, &sec->symbol_list, list) + if (sym->type == STT_FUNC && sym->offset == offset) return sym; return NULL; diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index 44150204db4d..a1963259b930 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -77,6 +77,7 @@ struct elf { struct elf *elf_read(const char *name, int flags); struct section *find_section_by_name(struct elf *elf, const char *name); +struct symbol *find_func_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_name(struct elf *elf, const char *name); struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); From c5b8425514da595763850ae2f3ef78ababe4be80 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 10 Feb 2020 18:19:58 +0100 Subject: [PATCH 0436/2231] counter: stm32-timer-cnt: add power management support Add suspend/resume PM sleep ops. When going to low power, enforce the counter isn't active. Gracefully restore its state upon resume in case it's been left enabled prior to suspend. Acked-by: William Breathitt Gray Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/counter/stm32-timer-cnt.c | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 3eafccec3beb..50496f453d6d 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #define TIM_CCMR_CCXS (BIT(8) | BIT(0)) @@ -20,11 +21,20 @@ #define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \ TIM_CCER_CC2P | TIM_CCER_CC2NP) +struct stm32_timer_regs { + u32 cr1; + u32 cnt; + u32 smcr; + u32 arr; +}; + struct stm32_timer_cnt { struct counter_device counter; struct regmap *regmap; struct clk *clk; u32 ceiling; + bool enabled; + struct stm32_timer_regs bak; }; /** @@ -224,6 +234,9 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter, clk_disable(priv->clk); } + /* Keep enabled state to properly handle low power states */ + priv->enabled = enable; + return len; } @@ -358,10 +371,59 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) priv->counter.num_signals = ARRAY_SIZE(stm32_signals); priv->counter.priv = priv; + platform_set_drvdata(pdev, priv); + /* Register Counter device */ return devm_counter_register(dev, &priv->counter); } +static int __maybe_unused stm32_timer_cnt_suspend(struct device *dev) +{ + struct stm32_timer_cnt *priv = dev_get_drvdata(dev); + + /* Only take care of enabled counter: don't disturb other MFD child */ + if (priv->enabled) { + /* Backup registers that may get lost in low power mode */ + regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr); + regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr); + regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt); + regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1); + + /* Disable the counter */ + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + clk_disable(priv->clk); + } + + return pinctrl_pm_select_sleep_state(dev); +} + +static int __maybe_unused stm32_timer_cnt_resume(struct device *dev) +{ + struct stm32_timer_cnt *priv = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + return ret; + + if (priv->enabled) { + clk_enable(priv->clk); + + /* Restore registers that may have been lost */ + regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr); + regmap_write(priv->regmap, TIM_ARR, priv->bak.arr); + regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt); + + /* Also re-enables the counter */ + regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_timer_cnt_pm_ops, stm32_timer_cnt_suspend, + stm32_timer_cnt_resume); + static const struct of_device_id stm32_timer_cnt_of_match[] = { { .compatible = "st,stm32-timer-counter", }, {}, @@ -373,6 +435,7 @@ static struct platform_driver stm32_timer_cnt_driver = { .driver = { .name = "stm32-timer-counter", .of_match_table = stm32_timer_cnt_of_match, + .pm = &stm32_timer_cnt_pm_ops, }, }; module_platform_driver(stm32_timer_cnt_driver); From 15e8573d3b3f7f60b8b4e2c58e5bfc0413c9a89d Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Tue, 11 Feb 2020 11:56:06 +0100 Subject: [PATCH 0437/2231] counter: stm32-timer-cnt: remove iio headers The stm32-timer-cnt driver doesn't use the iio interface. The iio headers aren't relevant and can be removed as reported by William in [1]. With this change, mod_devicetable.h needs to be added to define the 'of_device_id' struct. [1] https://lkml.org/lkml/2020/2/10/1516 Reported-by: William Breathitt Gray Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/counter/stm32-timer-cnt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 50496f453d6d..ef2a974a2f10 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -8,9 +8,8 @@ * */ #include -#include -#include #include +#include #include #include #include From b8528224741b53100381b1382d35422f4b696abb Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Thu, 6 Feb 2020 17:11:45 +0200 Subject: [PATCH 0438/2231] iio: core: Handle 'dB' suffix in core This patch handles the db suffix used for writing micro db values. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 39 ++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 65ff0d067018..0b14666dff09 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -769,17 +769,18 @@ static ssize_t iio_read_channel_info_avail(struct device *dev, } /** - * iio_str_to_fixpoint() - Parse a fixed-point number from a string + * __iio_str_to_fixpoint() - Parse a fixed-point number from a string * @str: The string to parse * @fract_mult: Multiplier for the first decimal place, should be a power of 10 * @integer: The integer part of the number * @fract: The fractional part of the number + * @scale_db: True if this should parse as dB * * Returns 0 on success, or a negative error code if the string could not be * parsed. */ -int iio_str_to_fixpoint(const char *str, int fract_mult, - int *integer, int *fract) +static int __iio_str_to_fixpoint(const char *str, int fract_mult, + int *integer, int *fract, bool scale_db) { int i = 0, f = 0; bool integer_part = true, negative = false; @@ -810,6 +811,14 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, break; else return -EINVAL; + } else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) { + /* Ignore the dB suffix */ + str += sizeof(" dB") - 1; + continue; + } else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) { + /* Ignore the dB suffix */ + str += sizeof("dB") - 1; + continue; } else if (*str == '.' && integer_part) { integer_part = false; } else { @@ -830,6 +839,22 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, return 0; } + +/** + * iio_str_to_fixpoint() - Parse a fixed-point number from a string + * @str: The string to parse + * @fract_mult: Multiplier for the first decimal place, should be a power of 10 + * @integer: The integer part of the number + * @fract: The fractional part of the number + * + * Returns 0 on success, or a negative error code if the string could not be + * parsed. + */ +int iio_str_to_fixpoint(const char *str, int fract_mult, + int *integer, int *fract) +{ + return __iio_str_to_fixpoint(str, fract_mult, integer, fract, false); +} EXPORT_SYMBOL_GPL(iio_str_to_fixpoint); static ssize_t iio_write_channel_info(struct device *dev, @@ -842,6 +867,7 @@ static ssize_t iio_write_channel_info(struct device *dev, int ret, fract_mult = 100000; int integer, fract = 0; bool is_char = false; + bool scale_db = false; /* Assumes decimal - precision based on number of digits */ if (!indio_dev->info->write_raw) @@ -853,6 +879,9 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_INT: fract_mult = 0; break; + case IIO_VAL_INT_PLUS_MICRO_DB: + scale_db = true; + /* fall through */ case IIO_VAL_INT_PLUS_MICRO: fract_mult = 100000; break; @@ -877,6 +906,10 @@ static ssize_t iio_write_channel_info(struct device *dev, if (ret) return ret; } + ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract, + scale_db); + if (ret) + return ret; ret = indio_dev->info->write_raw(indio_dev, this_attr->c, integer, fract, this_attr->address); From 0cc97f2e46e0b374bbf7efc30bad638d8974cf1e Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Thu, 6 Feb 2020 17:11:46 +0200 Subject: [PATCH 0439/2231] iio: amplifiers: ad8366: Add write_raw_get_fmt function This patch add write_raw_get_fmt function to specify conversion for hardware gain. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 0176d3d8cc9c..95972ab60f42 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -180,9 +180,22 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, return ret; } +static int ad8366_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + return IIO_VAL_INT_PLUS_MICRO_DB; + default: + return -EINVAL; + } +} + static const struct iio_info ad8366_info = { .read_raw = &ad8366_read_raw, .write_raw = &ad8366_write_raw, + .write_raw_get_fmt = &ad8366_write_raw_get_fmt, }; #define AD8366_CHAN(_channel) { \ From c7225494be79f8629c9166b106d6b1febf2a882f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Feb 2020 12:34:05 +0100 Subject: [PATCH 0440/2231] efi/arm: Work around missing cache maintenance in decompressor handover The EFI stub executes within the context of the zImage as it was loaded by the firmware, which means it is treated as an ordinary PE/COFF executable, which is loaded into memory, and cleaned to the PoU to ensure that it can be executed safely while the MMU and caches are on. When the EFI stub hands over to the decompressor, we clean the caches by set/way and disable the MMU and D-cache, to comply with the Linux boot protocol for ARM. However, cache maintenance by set/way is not sufficient to ensure that subsequent instruction fetches and data accesses done with the MMU off see the correct data. This means that proceeding as we do currently is not safe, especially since we also perform data accesses with the MMU off, from a literal pool as well as the stack. So let's kick this can down the road a bit, and jump into the relocated zImage before disabling the caches. This removes the requirement to perform any by-VA cache maintenance on the original PE/COFF executable, but it does require that the relocated zImage is cleaned to the PoC, which is currently not the case. This will be addressed in a subsequent patch. Signed-off-by: Ard Biesheuvel --- arch/arm/boot/compressed/head.S | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 088b0a060876..39f7071d47c7 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -1461,6 +1461,17 @@ ENTRY(efi_stub_entry) @ Preserve return value of efi_entry() in r4 mov r4, r0 bl cache_clean_flush + + @ The PE/COFF loader might not have cleaned the code we are + @ running beyond the PoU, and so calling cache_off below from + @ inside the PE/COFF loader allocated region is unsafe. Let's + @ assume our own zImage relocation code did a better job, and + @ jump into its version of this routine before proceeding. + ldr r0, [sp] @ relocated zImage + ldr r1, .Ljmp + sub r1, r0, r1 + mov pc, r1 @ no mode switch +0: bl cache_off @ Set parameters for booting zImage according to boot protocol @@ -1469,18 +1480,15 @@ ENTRY(efi_stub_entry) mov r0, #0 mov r1, #0xFFFFFFFF mov r2, r4 - - @ Branch to (possibly) relocated zImage that is in [sp] - ldr lr, [sp] - ldr ip, =start_offset - add lr, lr, ip - mov pc, lr @ no mode switch + b __efi_start efi_load_fail: @ Return EFI_LOAD_ERROR to EFI firmware on error. ldr r0, =0x80000001 ldmfd sp!, {ip, pc} ENDPROC(efi_stub_entry) + .align 2 +.Ljmp: .long start - 0b #endif .align From e951a1f427f2312e17b4e0f485e60068ca1423bb Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 19 Feb 2020 00:09:48 +0100 Subject: [PATCH 0441/2231] efi/arm: Pass start and end addresses to cache_clean_flush() In preparation for turning the decompressor's cache clean/flush operations into proper by-VA maintenance for v7 cores, pass the start and end addresses of the regions that need cache maintenance into cache_clean_flush in registers r0 and r1. Currently, all implementations of cache_clean_flush ignore these values, so no functional change is expected as a result of this patch. Signed-off-by: Ard Biesheuvel --- arch/arm/boot/compressed/head.S | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 39f7071d47c7..8487221bedb0 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -1460,6 +1460,12 @@ ENTRY(efi_stub_entry) @ Preserve return value of efi_entry() in r4 mov r4, r0 + add r1, r4, #SZ_2M @ DT end + bl cache_clean_flush + + ldr r0, [sp] @ relocated zImage + ldr r1, =_edata @ size of zImage + add r1, r1, r0 @ end of zImage bl cache_clean_flush @ The PE/COFF loader might not have cleaned the code we are From 9f9223778ef385e79dc67f5ee48ee4c1fb757f6b Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 17 Feb 2020 12:44:37 +0100 Subject: [PATCH 0442/2231] efi/libstub/arm: Make efi_entry() an ordinary PE/COFF entrypoint Expose efi_entry() as the PE/COFF entrypoint directly, instead of jumping into a wrapper that fiddles with stack buffers and other stuff that the compiler is much better at. The only reason this code exists is to obtain a pointer to the base of the image, but we can get the same value from the loaded_image protocol, which we already need for other reasons anyway. Update the return type as well, to make it consistent with what is required for a PE/COFF executable entrypoint. Signed-off-by: Ard Biesheuvel --- arch/arm/boot/compressed/efi-header.S | 2 +- arch/arm/boot/compressed/head.S | 40 +++-------- arch/arm64/kernel/efi-entry.S | 86 +++++------------------ arch/arm64/kernel/efi-header.S | 2 +- arch/arm64/kernel/image-vars.h | 4 +- drivers/firmware/efi/libstub/arm-stub.c | 46 ++++++------ drivers/firmware/efi/libstub/arm32-stub.c | 1 + drivers/firmware/efi/libstub/arm64-stub.c | 3 +- 8 files changed, 58 insertions(+), 126 deletions(-) diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S index a5983588f96b..e38fbda02b93 100644 --- a/arch/arm/boot/compressed/efi-header.S +++ b/arch/arm/boot/compressed/efi-header.S @@ -60,7 +60,7 @@ optional_header: .long __pecoff_code_size @ SizeOfCode .long __pecoff_data_size @ SizeOfInitializedData .long 0 @ SizeOfUninitializedData - .long efi_stub_entry - start @ AddressOfEntryPoint + .long efi_entry - start @ AddressOfEntryPoint .long start_offset @ BaseOfCode .long __pecoff_data_start - start @ BaseOfData diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 8487221bedb0..36ffbeecd30b 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -1437,33 +1437,15 @@ __enter_kernel: reloc_code_end: #ifdef CONFIG_EFI_STUB - .align 2 -_start: .long start - . +ENTRY(efi_enter_kernel) + mov r7, r0 @ preserve image base + mov r4, r1 @ preserve DT pointer -ENTRY(efi_stub_entry) - @ allocate space on stack for passing current zImage address - @ and for the EFI stub to return of new entry point of - @ zImage, as EFI stub may copy the kernel. Pointer address - @ is passed in r2. r0 and r1 are passed through from the - @ EFI firmware to efi_entry - adr ip, _start - ldr r3, [ip] - add r3, r3, ip - stmfd sp!, {r3, lr} - mov r2, sp @ pass zImage address in r2 - bl efi_entry - - @ Check for error return from EFI stub. r0 has FDT address - @ or error code. - cmn r0, #1 - beq efi_load_fail - - @ Preserve return value of efi_entry() in r4 - mov r4, r0 - add r1, r4, #SZ_2M @ DT end + mov r0, r4 @ DT start + add r1, r4, r2 @ DT end bl cache_clean_flush - ldr r0, [sp] @ relocated zImage + mov r0, r7 @ relocated zImage ldr r1, =_edata @ size of zImage add r1, r1, r0 @ end of zImage bl cache_clean_flush @@ -1473,9 +1455,8 @@ ENTRY(efi_stub_entry) @ inside the PE/COFF loader allocated region is unsafe. Let's @ assume our own zImage relocation code did a better job, and @ jump into its version of this routine before proceeding. - ldr r0, [sp] @ relocated zImage ldr r1, .Ljmp - sub r1, r0, r1 + sub r1, r7, r1 mov pc, r1 @ no mode switch 0: bl cache_off @@ -1487,12 +1468,7 @@ ENTRY(efi_stub_entry) mov r1, #0xFFFFFFFF mov r2, r4 b __efi_start - -efi_load_fail: - @ Return EFI_LOAD_ERROR to EFI firmware on error. - ldr r0, =0x80000001 - ldmfd sp!, {ip, pc} -ENDPROC(efi_stub_entry) +ENDPROC(efi_enter_kernel) .align 2 .Ljmp: .long start - 0b #endif diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S index 304d5b02ca67..4cfd03c35c49 100644 --- a/arch/arm64/kernel/efi-entry.S +++ b/arch/arm64/kernel/efi-entry.S @@ -10,81 +10,35 @@ #include -#define EFI_LOAD_ERROR 0x8000000000000001 - __INIT - /* - * We arrive here from the EFI boot manager with: - * - * * CPU in little-endian mode - * * MMU on with identity-mapped RAM - * * Icache and Dcache on - * - * We will most likely be running from some place other than where - * we want to be. The kernel image wants to be placed at TEXT_OFFSET - * from start of RAM. - */ -ENTRY(entry) - /* - * Create a stack frame to save FP/LR with extra space - * for image_addr variable passed to efi_entry(). - */ - stp x29, x30, [sp, #-32]! - mov x29, sp - - /* - * Call efi_entry to do the real work. - * x0 and x1 are already set up by firmware. Current runtime - * address of image is calculated and passed via *image_addr. - * - * unsigned long efi_entry(void *handle, - * efi_system_table_t *sys_table, - * unsigned long *image_addr) ; - */ - adr_l x8, _text - add x2, sp, 16 - str x8, [x2] - bl efi_entry - cmn x0, #1 - b.eq efi_load_fail - +ENTRY(efi_enter_kernel) /* * efi_entry() will have copied the kernel image if necessary and we - * return here with device tree address in x0 and the kernel entry - * point stored at *image_addr. Save those values in registers which - * are callee preserved. + * end up here with device tree address in x1 and the kernel entry + * point stored in x0. Save those values in registers which are + * callee preserved. */ - mov x20, x0 // DTB address - ldr x0, [sp, #16] // relocated _text address - ldr w21, =stext_offset - add x21, x0, x21 - - /* - * Calculate size of the kernel Image (same for original and copy). - */ - adr_l x1, _text - adr_l x2, _edata - sub x1, x2, x1 + mov x19, x0 // relocated Image address + mov x20, x1 // DTB address /* * Flush the copied Image to the PoC, and ensure it is not shadowed by * stale icache entries from before relocation. */ + ldr w1, =kernel_size bl __flush_dcache_area ic ialluis + dsb sy /* - * Ensure that the rest of this function (in the original Image) is - * visible when the caches are disabled. The I-cache can't have stale - * entries for the VA range of the current image, so no maintenance is - * necessary. + * Jump across, into the copy of the image that we just cleaned + * to the PoC, so that we can safely disable the MMU and caches. */ - adr x0, entry - adr x1, entry_end - sub x1, x1, x0 - bl __flush_dcache_area - + ldr w0, .Ljmp + sub x0, x19, w0, sxtw + br x0 +0: /* Turn off Dcache and MMU */ mrs x0, CurrentEL cmp x0, #CurrentEL_EL2 @@ -109,12 +63,6 @@ ENTRY(entry) mov x1, xzr mov x2, xzr mov x3, xzr - br x21 - -efi_load_fail: - mov x0, #EFI_LOAD_ERROR - ldp x29, x30, [sp], #32 - ret - -entry_end: -ENDPROC(entry) + b stext +ENDPROC(efi_enter_kernel) +.Ljmp: .long _text - 0b diff --git a/arch/arm64/kernel/efi-header.S b/arch/arm64/kernel/efi-header.S index a7cfacce3e15..40c704c5d3a5 100644 --- a/arch/arm64/kernel/efi-header.S +++ b/arch/arm64/kernel/efi-header.S @@ -27,7 +27,7 @@ optional_header: .long __initdata_begin - efi_header_end // SizeOfCode .long __pecoff_data_size // SizeOfInitializedData .long 0 // SizeOfUninitializedData - .long __efistub_entry - _head // AddressOfEntryPoint + .long __efistub_efi_entry - _head // AddressOfEntryPoint .long efi_header_end - _head // BaseOfCode extra_header_fields: diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index 25a2a9b479c2..87cb3d45b4bd 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -12,7 +12,8 @@ #ifdef CONFIG_EFI -__efistub_stext_offset = stext - _text; +__efistub_kernel_size = _edata - _text; + /* * The EFI stub has its own symbol namespace prefixed by __efistub_, to @@ -42,6 +43,7 @@ __efistub___memset = __pi_memset; #endif __efistub__text = _text; +__efistub_stext = stext; __efistub__end = _end; __efistub__edata = _edata; __efistub_screen_info = screen_info; diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 7bbef4a67350..f12736f535b3 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -100,17 +101,22 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *reserve_size, unsigned long dram_base, efi_loaded_image_t *image); + +asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, + unsigned long fdt_addr, + unsigned long fdt_size); + /* * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint * that is described in the PE/COFF header. Most of the code is the same * for both archictectures, with the arch-specific code provided in the * handle_kernel_image() function. */ -unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, - unsigned long *image_addr) +efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) { efi_loaded_image_t *image; efi_status_t status; + unsigned long image_addr; unsigned long image_size = 0; unsigned long dram_base; /* addr/point and size pairs for memory management*/ @@ -120,7 +126,6 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, unsigned long fdt_size = 0; char *cmdline_ptr = NULL; int cmdline_size = 0; - unsigned long new_fdt_addr; efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; unsigned long reserve_addr = 0; unsigned long reserve_size = 0; @@ -130,8 +135,10 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, sys_table = sys_table_arg; /* Check if we were booted by the EFI firmware */ - if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { + status = EFI_INVALID_PARAMETER; goto fail; + } status = check_platform_features(); if (status != EFI_SUCCESS) @@ -152,6 +159,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, dram_base = get_dram_base(); if (dram_base == EFI_ERROR) { pr_efi_err("Failed to find DRAM base\n"); + status = EFI_LOAD_ERROR; goto fail; } @@ -163,6 +171,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, cmdline_ptr = efi_convert_cmdline(image, &cmdline_size); if (!cmdline_ptr) { pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); + status = EFI_OUT_OF_RESOURCES; goto fail; } @@ -178,7 +187,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, si = setup_graphics(); - status = handle_kernel_image(image_addr, &image_size, + status = handle_kernel_image(&image_addr, &image_size, &reserve_addr, &reserve_size, dram_base, image); @@ -227,7 +236,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, status = handle_cmdline_files(image, cmdline_ptr, "initrd=", efi_get_max_initrd_addr(dram_base, - *image_addr), + image_addr), (unsigned long *)&initrd_addr, (unsigned long *)&initrd_size); if (status != EFI_SUCCESS) @@ -257,33 +266,30 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, install_memreserve_table(); - new_fdt_addr = fdt_addr; - status = allocate_new_fdt_and_exit_boot(handle, - &new_fdt_addr, efi_get_max_fdt_addr(dram_base), - initrd_addr, initrd_size, cmdline_ptr, - fdt_addr, fdt_size); + status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr, + efi_get_max_fdt_addr(dram_base), + initrd_addr, initrd_size, + cmdline_ptr, fdt_addr, fdt_size); + if (status != EFI_SUCCESS) + goto fail_free_initrd; - /* - * If all went well, we need to return the FDT address to the - * calling function so it can be passed to kernel as part of - * the kernel boot protocol. - */ - if (status == EFI_SUCCESS) - return new_fdt_addr; + efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr)); + /* not reached */ +fail_free_initrd: pr_efi_err("Failed to update FDT and exit boot services\n"); efi_free(initrd_size, initrd_addr); efi_free(fdt_size, fdt_addr); fail_free_image: - efi_free(image_size, *image_addr); + efi_free(image_size, image_addr); efi_free(reserve_size, reserve_addr); fail_free_cmdline: free_screen_info(si); efi_free(cmdline_size, (unsigned long)cmdline_ptr); fail: - return EFI_ERROR; + return status; } static int cmp_mem_desc(const void *l, const void *r) diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index 7b2a6382b647..7826553af2ba 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -227,6 +227,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, * Relocate the zImage, so that it appears in the lowest 128 MB * memory window. */ + *image_addr = (unsigned long)image->image_base; *image_size = image->image_size; status = efi_relocate_kernel(image_addr, *image_size, *image_size, kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0); diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 2915b44132e6..ad5f24a4489b 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -49,7 +49,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, { efi_status_t status; unsigned long kernel_size, kernel_memsize = 0; - void *old_image_addr = (void *)*image_addr; unsigned long preferred_offset; u64 phys_seed = 0; @@ -147,7 +146,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, } *image_addr = *reserve_addr + TEXT_OFFSET; } - memcpy((void *)*image_addr, old_image_addr, kernel_size); + memcpy((void *)*image_addr, image->image_base, kernel_size); return EFI_SUCCESS; } From 67a6af7ad1d161dbc9c139e868d5549e632923f7 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sun, 2 Feb 2020 12:13:47 -0500 Subject: [PATCH 0443/2231] x86/boot: Remove KEEP_SEGMENTS support Commit a24e785111a3 ("i386: paravirt boot sequence") added this flag for use by paravirtualized environments such as Xen. However, Xen never made use of this flag [1], and it was only ever used by lguest [2]. Commit ecda85e70277 ("x86/lguest: Remove lguest support") removed lguest, so KEEP_SEGMENTS has lost its last user. [1] https://lore.kernel.org/lkml/4D4B097C.5050405@goop.org [2] https://www.mail-archive.com/lguest@lists.ozlabs.org/msg00469.html Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200202171353.3736319-2-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- Documentation/x86/boot.rst | 8 ++------ arch/x86/boot/compressed/head_32.S | 8 -------- arch/x86/boot/compressed/head_64.S | 8 -------- arch/x86/kernel/head_32.S | 6 ------ 4 files changed, 2 insertions(+), 28 deletions(-) diff --git a/Documentation/x86/boot.rst b/Documentation/x86/boot.rst index c9c201596c3e..fa7ddc0428c8 100644 --- a/Documentation/x86/boot.rst +++ b/Documentation/x86/boot.rst @@ -490,15 +490,11 @@ Protocol: 2.00+ kernel) to not write early messages that require accessing the display hardware directly. - Bit 6 (write): KEEP_SEGMENTS + Bit 6 (obsolete): KEEP_SEGMENTS Protocol: 2.07+ - - If 0, reload the segment registers in the 32bit entry point. - - If 1, do not reload the segment registers in the 32bit entry point. - - Assume that %cs %ds %ss %es are all set to flat segments with - a base of 0 (or the equivalent for their environment). + - This flag is obsolete. Bit 7 (write): CAN_USE_HEAP diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S index 73f17d0544dd..cb2cb91fce45 100644 --- a/arch/x86/boot/compressed/head_32.S +++ b/arch/x86/boot/compressed/head_32.S @@ -63,13 +63,6 @@ __HEAD SYM_FUNC_START(startup_32) cld - /* - * Test KEEP_SEGMENTS flag to see if the bootloader is asking - * us to not reload segments - */ - testb $KEEP_SEGMENTS, BP_loadflags(%esi) - jnz 1f - cli movl $__BOOT_DS, %eax movl %eax, %ds @@ -77,7 +70,6 @@ SYM_FUNC_START(startup_32) movl %eax, %fs movl %eax, %gs movl %eax, %ss -1: /* * Calculate the delta between where we were compiled to run diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index 1f1f6c8139b3..bd44d89540d3 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -53,19 +53,11 @@ SYM_FUNC_START(startup_32) * all need to be under the 4G limit. */ cld - /* - * Test KEEP_SEGMENTS flag to see if the bootloader is asking - * us to not reload segments - */ - testb $KEEP_SEGMENTS, BP_loadflags(%esi) - jnz 1f - cli movl $(__BOOT_DS), %eax movl %eax, %ds movl %eax, %es movl %eax, %ss -1: /* * Calculate the delta between where we were compiled to run diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S index 3923ab4630d7..f66a6b90f954 100644 --- a/arch/x86/kernel/head_32.S +++ b/arch/x86/kernel/head_32.S @@ -67,11 +67,6 @@ __HEAD SYM_CODE_START(startup_32) movl pa(initial_stack),%ecx - /* test KEEP_SEGMENTS flag to see if the bootloader is asking - us to not reload segments */ - testb $KEEP_SEGMENTS, BP_loadflags(%esi) - jnz 2f - /* * Set segments to known values. */ @@ -82,7 +77,6 @@ SYM_CODE_START(startup_32) movl %eax,%fs movl %eax,%gs movl %eax,%ss -2: leal -__PAGE_OFFSET(%ecx),%esp /* From 90ff226281e1083988a42cfc51f89d91734cc55e Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sun, 2 Feb 2020 12:13:48 -0500 Subject: [PATCH 0444/2231] efi/x86: Don't depend on firmware GDT layout When booting in mixed mode, the firmware's GDT is still installed at handover entry in efi32_stub_entry. We save the GDTR for later use in __efi64_thunk but we are assuming that descriptor 2 (__KERNEL_CS) is a valid 32-bit code segment descriptor and that descriptor 3 (__KERNEL_DS/__BOOT_DS) is a valid data segment descriptor. This happens to be true for OVMF (it actually uses descriptor 1 for data segments, but descriptor 3 is also setup as data), but we shouldn't depend on this being the case. Fix this by saving the code and data selectors in addition to the GDTR in efi32_stub_entry, and restoring them in __efi64_thunk before calling the firmware. The UEFI specification guarantees that selectors will be flat, so using the DS selector for all the segment registers should be enough. We also need to install our own GDT before initializing segment registers in startup_32, so move the GDT load up to the beginning of the function. [ardb: mention mixed mode in the commit log] Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200202171353.3736319-3-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/efi_thunk_64.S | 29 +++++++++++++++++++----- arch/x86/boot/compressed/head_64.S | 30 +++++++++++++++---------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/arch/x86/boot/compressed/efi_thunk_64.S b/arch/x86/boot/compressed/efi_thunk_64.S index 8fb7f6799c52..2b2049259619 100644 --- a/arch/x86/boot/compressed/efi_thunk_64.S +++ b/arch/x86/boot/compressed/efi_thunk_64.S @@ -54,11 +54,16 @@ SYM_FUNC_START(__efi64_thunk) * Switch to gdt with 32-bit segments. This is the firmware GDT * that was installed when the kernel started executing. This * pointer was saved at the EFI stub entry point in head_64.S. + * + * Pass the saved DS selector to the 32-bit code, and use far return to + * restore the saved CS selector. */ leaq efi32_boot_gdt(%rip), %rax lgdt (%rax) - pushq $__KERNEL_CS + movzwl efi32_boot_ds(%rip), %edx + movzwq efi32_boot_cs(%rip), %rax + pushq %rax leaq efi_enter32(%rip), %rax pushq %rax lretq @@ -73,6 +78,10 @@ SYM_FUNC_START(__efi64_thunk) movl %ebx, %es pop %rbx movl %ebx, %ds + /* Clear out 32-bit selector from FS and GS */ + xorl %ebx, %ebx + movl %ebx, %fs + movl %ebx, %gs /* * Convert 32-bit status code into 64-bit. @@ -92,10 +101,12 @@ SYM_FUNC_END(__efi64_thunk) * The stack should represent the 32-bit calling convention. */ SYM_FUNC_START_LOCAL(efi_enter32) - movl $__KERNEL_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %ss + /* Load firmware selector into data and stack segment registers */ + movl %edx, %ds + movl %edx, %es + movl %edx, %fs + movl %edx, %gs + movl %edx, %ss /* Reload pgtables */ movl %cr3, %eax @@ -157,6 +168,14 @@ SYM_DATA_START(efi32_boot_gdt) .quad 0 SYM_DATA_END(efi32_boot_gdt) +SYM_DATA_START(efi32_boot_cs) + .word 0 +SYM_DATA_END(efi32_boot_cs) + +SYM_DATA_START(efi32_boot_ds) + .word 0 +SYM_DATA_END(efi32_boot_ds) + SYM_DATA_START(efi_gdt64) .word efi_gdt64_end - efi_gdt64 .long 0 /* Filled out by user */ diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index bd44d89540d3..c56b30bd9c7b 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -54,10 +54,6 @@ SYM_FUNC_START(startup_32) */ cld cli - movl $(__BOOT_DS), %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %ss /* * Calculate the delta between where we were compiled to run @@ -72,10 +68,20 @@ SYM_FUNC_START(startup_32) 1: popl %ebp subl $1b, %ebp + /* Load new GDT with the 64bit segments using 32bit descriptor */ + addl %ebp, gdt+2(%ebp) + lgdt gdt(%ebp) + + /* Load segment registers with our descriptors */ + movl $__BOOT_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + /* setup a stack and make sure cpu supports long mode. */ - movl $boot_stack_end, %eax - addl %ebp, %eax - movl %eax, %esp + leal boot_stack_end(%ebp), %esp call verify_cpu testl %eax, %eax @@ -112,10 +118,6 @@ SYM_FUNC_START(startup_32) * Prepare for entering 64 bit mode */ - /* Load new GDT with the 64bit segments using 32bit descriptor */ - addl %ebp, gdt+2(%ebp) - lgdt gdt(%ebp) - /* Enable PAE mode */ movl %cr4, %eax orl $X86_CR4_PAE, %eax @@ -232,9 +234,13 @@ SYM_FUNC_START(efi32_stub_entry) movl %ecx, efi32_boot_args(%ebp) movl %edx, efi32_boot_args+4(%ebp) - sgdtl efi32_boot_gdt(%ebp) movb $0, efi_is64(%ebp) + /* Save firmware GDTR and code/data selectors */ + sgdtl efi32_boot_gdt(%ebp) + movw %cs, efi32_boot_cs(%ebp) + movw %ds, efi32_boot_ds(%ebp) + /* Disable paging */ movl %cr0, %eax btrl $X86_CR0_PG_BIT, %eax From 32d009137a5646947d450da6fa641a1f4dc1e42c Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sun, 2 Feb 2020 12:13:49 -0500 Subject: [PATCH 0445/2231] x86/boot: Reload GDTR after copying to the end of the buffer The GDT may get overwritten during the copy or during extract_kernel, which will cause problems if any segment register is touched before the GDTR is reloaded by the decompressed kernel. For safety update the GDTR to point to the GDT within the copied kernel. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200202171353.3736319-4-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/head_64.S | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index c56b30bd9c7b..27eb2a6786db 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -439,6 +439,16 @@ trampoline_return: cld popq %rsi + /* + * The GDT may get overwritten either during the copy we just did or + * during extract_kernel below. To avoid any issues, repoint the GDTR + * to the new copy of the GDT. + */ + leaq gdt64(%rbx), %rax + subq %rbp, 2(%rax) + addq %rbx, 2(%rax) + lgdt (%rax) + /* * Jump to the relocated address. */ From cae0e431a02cd63fecaf677ae166f184644125a7 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sun, 2 Feb 2020 12:13:50 -0500 Subject: [PATCH 0446/2231] x86/boot: Clear direction and interrupt flags in startup_64 startup_32 already clears these flags on entry, do it in startup_64 as well for consistency. The direction flag in particular is not specified to be cleared in the boot protocol documentation, and we currently call into C code (paging_prepare) without explicitly clearing it. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200202171353.3736319-5-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/head_64.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index 27eb2a6786db..69cc6c68741e 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -264,6 +264,9 @@ SYM_CODE_START(startup_64) * and command line. */ + cld + cli + /* Setup data segments. */ xorl %eax, %eax movl %eax, %ds From ef5a7b5eb13ed88ba9690ab27def3a085332cc8c Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sun, 2 Feb 2020 12:13:51 -0500 Subject: [PATCH 0447/2231] efi/x86: Remove GDT setup from efi_main The 64-bit kernel will already load a GDT in startup_64, which is the next function to execute after return from efi_main. Add GDT setup code to the 32-bit kernel's startup_32 as well. Doing it in the head code has the advantage that we can avoid potentially corrupting the GDT during copy/decompression. This also removes dependence on having a specific GDT layout setup by the bootloader. Both startup_32 and startup_64 now clear interrupts on entry, so we can remove that from efi_main as well. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200202171353.3736319-6-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/eboot.c | 103 ----------------------------- arch/x86/boot/compressed/head_32.S | 40 +++++++++-- 2 files changed, 34 insertions(+), 109 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 287393d725f0..c92fe0b75cec 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -712,10 +712,8 @@ struct boot_params *efi_main(efi_handle_t handle, efi_system_table_t *sys_table_arg, struct boot_params *boot_params) { - struct desc_ptr *gdt = NULL; struct setup_header *hdr = &boot_params->hdr; efi_status_t status; - struct desc_struct *desc; unsigned long cmdline_paddr; sys_table = sys_table_arg; @@ -753,20 +751,6 @@ struct boot_params *efi_main(efi_handle_t handle, setup_quirks(boot_params); - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*gdt), - (void **)&gdt); - if (status != EFI_SUCCESS) { - efi_printk("Failed to allocate memory for 'gdt' structure\n"); - goto fail; - } - - gdt->size = 0x800; - status = efi_low_alloc(gdt->size, 8, (unsigned long *)&gdt->address); - if (status != EFI_SUCCESS) { - efi_printk("Failed to allocate memory for 'gdt'\n"); - goto fail; - } - /* * If the kernel isn't already loaded at the preferred load * address, relocate it. @@ -793,93 +777,6 @@ struct boot_params *efi_main(efi_handle_t handle, goto fail; } - memset((char *)gdt->address, 0x0, gdt->size); - desc = (struct desc_struct *)gdt->address; - - /* The first GDT is a dummy. */ - desc++; - - if (IS_ENABLED(CONFIG_X86_64)) { - /* __KERNEL32_CS */ - desc->limit0 = 0xffff; - desc->base0 = 0x0000; - desc->base1 = 0x0000; - desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; - desc->s = DESC_TYPE_CODE_DATA; - desc->dpl = 0; - desc->p = 1; - desc->limit1 = 0xf; - desc->avl = 0; - desc->l = 0; - desc->d = SEG_OP_SIZE_32BIT; - desc->g = SEG_GRANULARITY_4KB; - desc->base2 = 0x00; - - desc++; - } else { - /* Second entry is unused on 32-bit */ - desc++; - } - - /* __KERNEL_CS */ - desc->limit0 = 0xffff; - desc->base0 = 0x0000; - desc->base1 = 0x0000; - desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; - desc->s = DESC_TYPE_CODE_DATA; - desc->dpl = 0; - desc->p = 1; - desc->limit1 = 0xf; - desc->avl = 0; - - if (IS_ENABLED(CONFIG_X86_64)) { - desc->l = 1; - desc->d = 0; - } else { - desc->l = 0; - desc->d = SEG_OP_SIZE_32BIT; - } - desc->g = SEG_GRANULARITY_4KB; - desc->base2 = 0x00; - desc++; - - /* __KERNEL_DS */ - desc->limit0 = 0xffff; - desc->base0 = 0x0000; - desc->base1 = 0x0000; - desc->type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; - desc->s = DESC_TYPE_CODE_DATA; - desc->dpl = 0; - desc->p = 1; - desc->limit1 = 0xf; - desc->avl = 0; - desc->l = 0; - desc->d = SEG_OP_SIZE_32BIT; - desc->g = SEG_GRANULARITY_4KB; - desc->base2 = 0x00; - desc++; - - if (IS_ENABLED(CONFIG_X86_64)) { - /* Task segment value */ - desc->limit0 = 0x0000; - desc->base0 = 0x0000; - desc->base1 = 0x0000; - desc->type = SEG_TYPE_TSS; - desc->s = 0; - desc->dpl = 0; - desc->p = 1; - desc->limit1 = 0x0; - desc->avl = 0; - desc->l = 0; - desc->d = 0; - desc->g = SEG_GRANULARITY_4KB; - desc->base2 = 0x00; - desc++; - } - - asm volatile("cli"); - asm volatile ("lgdt %0" : : "m" (*gdt)); - return boot_params; fail: efi_printk("efi_main() failed!\n"); diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S index cb2cb91fce45..356060c5332c 100644 --- a/arch/x86/boot/compressed/head_32.S +++ b/arch/x86/boot/compressed/head_32.S @@ -64,12 +64,6 @@ SYM_FUNC_START(startup_32) cld cli - movl $__BOOT_DS, %eax - movl %eax, %ds - movl %eax, %es - movl %eax, %fs - movl %eax, %gs - movl %eax, %ss /* * Calculate the delta between where we were compiled to run @@ -84,6 +78,19 @@ SYM_FUNC_START(startup_32) 1: popl %ebp subl $1b, %ebp + /* Load new GDT */ + leal gdt(%ebp), %eax + movl %eax, 2(%eax) + lgdt (%eax) + + /* Load segment registers with our descriptors */ + movl $__BOOT_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + /* * %ebp contains the address we are loaded at by the boot loader and %ebx * contains the address where we should move the kernel image temporarily @@ -129,6 +136,16 @@ SYM_FUNC_START(startup_32) cld popl %esi + /* + * The GDT may get overwritten either during the copy we just did or + * during extract_kernel below. To avoid any issues, repoint the GDTR + * to the new copy of the GDT. EAX still contains the previously + * calculated relocation offset of init_size - _end. + */ + leal gdt(%ebx), %edx + addl %eax, 2(%edx) + lgdt (%edx) + /* * Jump to the relocated address. */ @@ -201,6 +218,17 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated) jmp *%eax SYM_FUNC_END(.Lrelocated) + .data + .balign 8 +SYM_DATA_START_LOCAL(gdt) + .word gdt_end - gdt - 1 + .long 0 + .word 0 + .quad 0x0000000000000000 /* Reserved */ + .quad 0x00cf9a000000ffff /* __KERNEL_CS */ + .quad 0x00cf92000000ffff /* __KERNEL_DS */ +SYM_DATA_END_LABEL(gdt, SYM_L_LOCAL, gdt_end) + /* * Stack and heap for uncompression */ From b75e2b076d00751579c73cfbbc8a7eac7d2a0468 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sun, 2 Feb 2020 12:13:52 -0500 Subject: [PATCH 0448/2231] x86/boot: GDT limit value should be size - 1 The limit value for the GDTR should be such that adding it to the base address gives the address of the last byte of the GDT, i.e. it should be one less than the size, not the size. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200202171353.3736319-7-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/head_64.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index 69cc6c68741e..c36e6156b6a3 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -624,12 +624,12 @@ SYM_FUNC_END(.Lno_longmode) .data SYM_DATA_START_LOCAL(gdt64) - .word gdt_end - gdt + .word gdt_end - gdt - 1 .quad 0 SYM_DATA_END(gdt64) .balign 8 SYM_DATA_START_LOCAL(gdt) - .word gdt_end - gdt + .word gdt_end - gdt - 1 .long gdt .word 0 .quad 0x00cf9a000000ffff /* __KERNEL32_CS */ From 8a3abe30de9fffec8b44adeb78f93ecb0f09b0c5 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sun, 2 Feb 2020 12:13:53 -0500 Subject: [PATCH 0449/2231] x86/boot: Micro-optimize GDT loading instructions Rearrange the instructions a bit to use a 32-bit displacement once instead of 2/3 times. This saves 8 bytes of machine code. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200202171353.3736319-8-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/head_64.S | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index c36e6156b6a3..a4f5561c1c0e 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -69,8 +69,9 @@ SYM_FUNC_START(startup_32) subl $1b, %ebp /* Load new GDT with the 64bit segments using 32bit descriptor */ - addl %ebp, gdt+2(%ebp) - lgdt gdt(%ebp) + leal gdt(%ebp), %eax + movl %eax, 2(%eax) + lgdt (%eax) /* Load segment registers with our descriptors */ movl $__BOOT_DS, %eax @@ -355,9 +356,9 @@ SYM_CODE_START(startup_64) */ /* Make sure we have GDT with 32-bit code segment */ - leaq gdt(%rip), %rax - movq %rax, gdt64+2(%rip) - lgdt gdt64(%rip) + leaq gdt64(%rip), %rax + addq %rax, 2(%rax) + lgdt (%rax) /* * paging_prepare() sets up the trampoline and checks if we need to @@ -625,12 +626,12 @@ SYM_FUNC_END(.Lno_longmode) .data SYM_DATA_START_LOCAL(gdt64) .word gdt_end - gdt - 1 - .quad 0 + .quad gdt - gdt64 SYM_DATA_END(gdt64) .balign 8 SYM_DATA_START_LOCAL(gdt) .word gdt_end - gdt - 1 - .long gdt + .long 0 .word 0 .quad 0x00cf9a000000ffff /* __KERNEL32_CS */ .quad 0x00af9a000000ffff /* __KERNEL_CS */ From f32ea1cd124c9a8b847e33123d156cb55699fa51 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Jan 2020 17:20:04 -0500 Subject: [PATCH 0450/2231] efi/x86: Mark setup_graphics static This function is only called from efi_main in the same source file. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200130222004.1932152-1-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/eboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index c92fe0b75cec..32423e83ba8f 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -315,7 +315,7 @@ free_handle: return status; } -void setup_graphics(struct boot_params *boot_params) +static void setup_graphics(struct boot_params *boot_params) { efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; struct screen_info *si; From 55087c5713dcf1c6279d223e380cc79f8f9b4f23 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Jan 2020 14:06:23 +0100 Subject: [PATCH 0451/2231] efi/bgrt: Accept BGRT tables with a version of 0 Some (somewhat older) laptops have a correct BGRT table, except that the version field is 0 instead of 1. This has been seen on several Ivy Bridge based Lenovo models. For now the spec. only defines version 1, so it is reasonably safe to assume that tables with a version of 0 really are version 1 too, which is what this commit does so that the BGRT table will be accepted by the kernel on laptop models with this issue. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200131130623.33875-1-hdegoede@redhat.com Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi-bgrt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/efi-bgrt.c b/drivers/firmware/efi/efi-bgrt.c index b07c17643210..6aafdb67dbca 100644 --- a/drivers/firmware/efi/efi-bgrt.c +++ b/drivers/firmware/efi/efi-bgrt.c @@ -42,7 +42,12 @@ void __init efi_bgrt_init(struct acpi_table_header *table) return; } *bgrt = *(struct acpi_table_bgrt *)table; - if (bgrt->version != 1) { + /* + * Only version 1 is defined but some older laptops (seen on Lenovo + * Ivy Bridge models) have a correct version 1 BGRT table with the + * version set to 0, so we accept version 0 and 1. + */ + if (bgrt->version > 1) { pr_notice("Ignoring BGRT: invalid version %u (expected 1)\n", bgrt->version); goto out; From b92165d2ba869a9d0b43d00a5676c4b341194c88 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 19:29:36 +0100 Subject: [PATCH 0452/2231] efi/libstub/arm64: Use 1:1 mapping of RT services if property table exists The UEFI spec defines (and deprecates) a misguided and shortlived memory protection feature that is based on splitting memory regions covering PE/COFF executables into separate code and data regions, without annotating them as belonging to the same executable image. When the OS assigns the virtual addresses of these regions, it may move them around arbitrarily, without taking into account that the PE/COFF code sections may contain relative references into the data sections, which means the relative placement of these segments has to be preserved or the executable image will be corrupted. The original workaround on arm64 was to ensure that adjacent regions of the same type were mapped adjacently in the virtual mapping, but this requires sorting of the memory map, which we would prefer to avoid. Considering that the native physical mapping of the PE/COFF images does not suffer from this issue, let's preserve it at runtime, and install it as the virtual mapping as well. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 1 - drivers/firmware/efi/libstub/arm-stub.c | 84 +++++++------------------ 2 files changed, 22 insertions(+), 63 deletions(-) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 98a81576213d..f14b7636323a 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -43,7 +43,6 @@ lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ # include the stub's generic dependencies from lib/ when building for ARM/arm64 arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c -arm-deps-$(CONFIG_ARM64) += sort.c $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index f12736f535b3..e5c56306c641 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -11,7 +11,6 @@ #include #include -#include #include #include "efistub.h" @@ -37,6 +36,7 @@ #endif static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; +static bool __efistub_global flat_va_mapping; static efi_system_table_t *__efistub_global sys_table; @@ -131,6 +131,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) unsigned long reserve_size = 0; enum efi_secureboot_mode secure_boot; struct screen_info *si; + efi_properties_table_t *prop_tbl; sys_table = sys_table_arg; @@ -244,8 +245,20 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) efi_random_get_seed(); + /* + * If the NX PE data feature is enabled in the properties table, we + * should take care not to create a virtual mapping that changes the + * relative placement of runtime services code and data regions, as + * they may belong to the same PE/COFF executable image in memory. + * The easiest way to achieve that is to simply use a 1:1 mapping. + */ + prop_tbl = get_efi_config_table(EFI_PROPERTIES_TABLE_GUID); + flat_va_mapping = prop_tbl && + (prop_tbl->memory_protection_attribute & + EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); + /* hibernation expects the runtime regions to stay in the same place */ - if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr()) { + if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr() && !flat_va_mapping) { /* * Randomize the base of the UEFI runtime services region. * Preserve the 2 MB alignment of the region by taking a @@ -292,44 +305,6 @@ fail: return status; } -static int cmp_mem_desc(const void *l, const void *r) -{ - const efi_memory_desc_t *left = l, *right = r; - - return (left->phys_addr > right->phys_addr) ? 1 : -1; -} - -/* - * Returns whether region @left ends exactly where region @right starts, - * or false if either argument is NULL. - */ -static bool regions_are_adjacent(efi_memory_desc_t *left, - efi_memory_desc_t *right) -{ - u64 left_end; - - if (left == NULL || right == NULL) - return false; - - left_end = left->phys_addr + left->num_pages * EFI_PAGE_SIZE; - - return left_end == right->phys_addr; -} - -/* - * Returns whether region @left and region @right have compatible memory type - * mapping attributes, and are both EFI_MEMORY_RUNTIME regions. - */ -static bool regions_have_compatible_memory_type_attrs(efi_memory_desc_t *left, - efi_memory_desc_t *right) -{ - static const u64 mem_type_mask = EFI_MEMORY_WB | EFI_MEMORY_WT | - EFI_MEMORY_WC | EFI_MEMORY_UC | - EFI_MEMORY_RUNTIME; - - return ((left->attribute ^ right->attribute) & mem_type_mask) == 0; -} - /* * efi_get_virtmap() - create a virtual mapping for the EFI memory map * @@ -342,23 +317,10 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, int *count) { u64 efi_virt_base = virtmap_base; - efi_memory_desc_t *in, *prev = NULL, *out = runtime_map; + efi_memory_desc_t *in, *out = runtime_map; int l; - /* - * To work around potential issues with the Properties Table feature - * introduced in UEFI 2.5, which may split PE/COFF executable images - * in memory into several RuntimeServicesCode and RuntimeServicesData - * regions, we need to preserve the relative offsets between adjacent - * EFI_MEMORY_RUNTIME regions with the same memory type attributes. - * The easiest way to find adjacent regions is to sort the memory map - * before traversing it. - */ - if (IS_ENABLED(CONFIG_ARM64)) - sort(memory_map, map_size / desc_size, desc_size, cmp_mem_desc, - NULL); - - for (l = 0; l < map_size; l += desc_size, prev = in) { + for (l = 0; l < map_size; l += desc_size) { u64 paddr, size; in = (void *)memory_map + l; @@ -368,8 +330,8 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, paddr = in->phys_addr; size = in->num_pages * EFI_PAGE_SIZE; + in->virt_addr = in->phys_addr; if (novamap()) { - in->virt_addr = in->phys_addr; continue; } @@ -378,9 +340,7 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, * a 4k page size kernel to kexec a 64k page size kernel and * vice versa. */ - if ((IS_ENABLED(CONFIG_ARM64) && - !regions_are_adjacent(prev, in)) || - !regions_have_compatible_memory_type_attrs(prev, in)) { + if (!flat_va_mapping) { paddr = round_down(in->phys_addr, SZ_64K); size += in->phys_addr - paddr; @@ -395,10 +355,10 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, efi_virt_base = round_up(efi_virt_base, SZ_2M); else efi_virt_base = round_up(efi_virt_base, SZ_64K); - } - in->virt_addr = efi_virt_base + in->phys_addr - paddr; - efi_virt_base += size; + in->virt_addr += efi_virt_base - paddr; + efi_virt_base += size; + } memcpy(out, in, desc_size); out = (void *)out + desc_size; From 3b9274ea1c0279b369212ccd440c9d61efdfa562 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 11 Feb 2020 17:14:21 -0600 Subject: [PATCH 0453/2231] efi/apple-properties: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertenly introduced[3] to the codebase from now on. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200211231421.GA15697@embeddedor Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/apple-properties.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c index 5ccf39986a14..084942846f4d 100644 --- a/drivers/firmware/efi/apple-properties.c +++ b/drivers/firmware/efi/apple-properties.c @@ -31,7 +31,7 @@ __setup("dump_apple_properties", dump_properties_enable); struct dev_header { u32 len; u32 prop_count; - struct efi_dev_path path[0]; + struct efi_dev_path path[]; /* * followed by key/value pairs, each key and value preceded by u32 len, * len includes itself, value may be empty (in which case its len is 4) @@ -42,7 +42,7 @@ struct properties_header { u32 len; u32 version; u32 dev_count; - struct dev_header dev_header[0]; + struct dev_header dev_header[]; }; static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, From e6d832ea9ac63316ee72df5e9f21698cfd486698 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:30 +0100 Subject: [PATCH 0454/2231] efi/libstub/x86: Remove pointless zeroing of apm_bios_info We have some code in the EFI stub entry point that takes the address of the apm_bios_info struct in the newly allocated and zeroed out boot_params structure, only to zero it out again. This is pointless so remove it. Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/eboot.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 32423e83ba8f..4745a1ee7953 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -358,7 +358,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) { struct boot_params *boot_params; - struct apm_bios_info *bi; struct setup_header *hdr; efi_loaded_image_t *image; efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; @@ -389,7 +388,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, memset(boot_params, 0x0, 0x4000); hdr = &boot_params->hdr; - bi = &boot_params->apm_bios_info; /* Copy the second sector to boot_params */ memcpy(&hdr->jump, image->image_base + 512, 512); @@ -416,9 +414,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, hdr->ramdisk_image = 0; hdr->ramdisk_size = 0; - /* Clear APM BIOS info */ - memset(bi, 0, sizeof(*bi)); - status = efi_parse_options(cmdline_ptr); if (status != EFI_SUCCESS) goto fail2; From 04a7d0e15606769ef58d5cee912c5d08d93ded92 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:31 +0100 Subject: [PATCH 0455/2231] efi/libstub/x86: Avoid overflowing code32_start on PE entry When using the native PE entry point (as opposed to the EFI handover protocol entry point that is used more widely), we set code32_start, which is a 32-bit wide field, to the effective symbol address of startup_32, which could overflow given that the EFI loader may have located the running image anywhere in memory, and we haven't reached the point yet where we relocate ourselves. Since we relocate ourselves if code32_start != pref_address, this isn't likely to lead to problems in practice, given how unlikely it is that the truncated effective address of startup_32 happens to equal pref_address. But it is better to defer the assignment of code32_start to after the relocation, when it is guaranteed to fit. While at it, move the call to efi_relocate_kernel() to an earlier stage so it is more likely that our preferred offset in memory has not been occupied by other memory allocations done in the mean time. Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/eboot.c | 40 ++++++++++++++------------------ 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 4745a1ee7953..55637135b50c 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -439,8 +439,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32; boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32; - hdr->code32_start = (u32)(unsigned long)startup_32; - efi_stub_entry(handle, sys_table, boot_params); /* not reached */ @@ -707,6 +705,7 @@ struct boot_params *efi_main(efi_handle_t handle, efi_system_table_t *sys_table_arg, struct boot_params *boot_params) { + unsigned long bzimage_addr = (unsigned long)startup_32; struct setup_header *hdr = &boot_params->hdr; efi_status_t status; unsigned long cmdline_paddr; @@ -717,6 +716,23 @@ struct boot_params *efi_main(efi_handle_t handle, if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) goto fail; + /* + * If the kernel isn't already loaded at the preferred load + * address, relocate it. + */ + if (bzimage_addr != hdr->pref_address) { + status = efi_relocate_kernel(&bzimage_addr, + hdr->init_size, hdr->init_size, + hdr->pref_address, + hdr->kernel_alignment, + LOAD_PHYSICAL_ADDR); + if (status != EFI_SUCCESS) { + efi_printk("efi_relocate_kernel() failed!\n"); + goto fail; + } + } + hdr->code32_start = (u32)bzimage_addr; + /* * make_boot_params() may have been called before efi_main(), in which * case this is the second time we parse the cmdline. This is ok, @@ -746,26 +762,6 @@ struct boot_params *efi_main(efi_handle_t handle, setup_quirks(boot_params); - /* - * If the kernel isn't already loaded at the preferred load - * address, relocate it. - */ - if (hdr->pref_address != hdr->code32_start) { - unsigned long bzimage_addr = hdr->code32_start; - status = efi_relocate_kernel(&bzimage_addr, - hdr->init_size, hdr->init_size, - hdr->pref_address, - hdr->kernel_alignment, - LOAD_PHYSICAL_ADDR); - if (status != EFI_SUCCESS) { - efi_printk("efi_relocate_kernel() failed!\n"); - goto fail; - } - - hdr->pref_address = hdr->code32_start; - hdr->code32_start = bzimage_addr; - } - status = exit_boot(boot_params, handle); if (status != EFI_SUCCESS) { efi_printk("exit_boot() failed!\n"); From bb9397a356c6cc9a6e1d45633476fe83cf15a95f Mon Sep 17 00:00:00 2001 From: Tetsuhiro Kohada Date: Wed, 19 Feb 2020 14:57:27 +0900 Subject: [PATCH 0456/2231] staging: exfat: remove symlink feature. Remove symlink feature completely. Becouse -Uses reserved areas(defined in the Microsoft exfat specification), causing future incompatibilities. -Not described in Microsoft exfat specifications or SD standards. -For REMOVABLE media, causes incompatibility with other implementations. -Not supported by other major exfat drivers. -Not implemented symlink feature in linux FAT/VFAT. Remove this feature completely because of serious media compatibility issues. (Can't enable even with CONFIG) If you have any questions about this patch, please let me know. Reviewed-by: Takahiro Mori Acked-by: Valdis Kletnieks Signed-off-by: Tetsuhiro Kohada Link: https://lore.kernel.org/r/20200219055727.12867-1-Kohada.Tetsuhiro@dc.MitsubishiElectric.co.jp Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat_super.c | 450 ---------------------------- 1 file changed, 450 deletions(-) diff --git a/drivers/staging/exfat/exfat_super.c b/drivers/staging/exfat/exfat_super.c index b398114c2604..c7bc07e91c45 100644 --- a/drivers/staging/exfat/exfat_super.c +++ b/drivers/staging/exfat/exfat_super.c @@ -660,375 +660,6 @@ out: return ret; } -static int ffsReadFile(struct inode *inode, struct file_id_t *fid, void *buffer, - u64 count, u64 *rcount) -{ - s32 offset, sec_offset, clu_offset; - u32 clu; - int ret = 0; - sector_t LogSector; - u64 oneblkread, read_bytes; - struct buffer_head *tmp_bh = NULL; - struct super_block *sb = inode->i_sb; - struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info); - struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info); - - /* check the validity of the given file id */ - if (!fid) - return -EINVAL; - - /* check the validity of pointer parameters */ - if (!buffer) - return -EINVAL; - - /* acquire the lock for file system critical section */ - mutex_lock(&p_fs->v_mutex); - - /* check if the given file ID is opened */ - if (fid->type != TYPE_FILE) { - ret = -EPERM; - goto out; - } - - if (fid->rwoffset > fid->size) - fid->rwoffset = fid->size; - - if (count > (fid->size - fid->rwoffset)) - count = fid->size - fid->rwoffset; - - if (count == 0) { - if (rcount) - *rcount = 0; - ret = 0; - goto out; - } - - read_bytes = 0; - - while (count > 0) { - clu_offset = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); - clu = fid->start_clu; - - if (fid->flags == 0x03) { - clu += clu_offset; - } else { - /* hint information */ - if ((clu_offset > 0) && (fid->hint_last_off > 0) && - (clu_offset >= fid->hint_last_off)) { - clu_offset -= fid->hint_last_off; - clu = fid->hint_last_clu; - } - - while (clu_offset > 0) { - /* clu = exfat_fat_read(sb, clu); */ - if (exfat_fat_read(sb, clu, &clu) == -1) { - ret = -EIO; - goto out; - } - - clu_offset--; - } - } - - /* hint information */ - fid->hint_last_off = (s32)(fid->rwoffset >> - p_fs->cluster_size_bits); - fid->hint_last_clu = clu; - - /* byte offset in cluster */ - offset = (s32)(fid->rwoffset & (p_fs->cluster_size - 1)); - - /* sector offset in cluster */ - sec_offset = offset >> p_bd->sector_size_bits; - - /* byte offset in sector */ - offset &= p_bd->sector_size_mask; - - LogSector = START_SECTOR(clu) + sec_offset; - - oneblkread = (u64)(p_bd->sector_size - offset); - if (oneblkread > count) - oneblkread = count; - - if ((offset == 0) && (oneblkread == p_bd->sector_size)) { - if (sector_read(sb, LogSector, &tmp_bh, 1) != - 0) - goto err_out; - memcpy((char *)buffer + read_bytes, - (char *)tmp_bh->b_data, (s32)oneblkread); - } else { - if (sector_read(sb, LogSector, &tmp_bh, 1) != - 0) - goto err_out; - memcpy((char *)buffer + read_bytes, - (char *)tmp_bh->b_data + offset, - (s32)oneblkread); - } - count -= oneblkread; - read_bytes += oneblkread; - fid->rwoffset += oneblkread; - } - brelse(tmp_bh); - -/* How did this ever work and not leak a brlse()?? */ -err_out: - /* set the size of read bytes */ - if (rcount) - *rcount = read_bytes; - - if (p_fs->dev_ejected) - ret = -EIO; - -out: - /* release the lock for file system critical section */ - mutex_unlock(&p_fs->v_mutex); - - return ret; -} - -static int ffsWriteFile(struct inode *inode, struct file_id_t *fid, - void *buffer, u64 count, u64 *wcount) -{ - bool modified = false; - s32 offset, sec_offset, clu_offset; - s32 num_clusters, num_alloc, num_alloced = (s32)~0; - int ret = 0; - u32 clu, last_clu; - sector_t LogSector; - u64 oneblkwrite, write_bytes; - struct chain_t new_clu; - struct timestamp_t tm; - struct dentry_t *ep, *ep2; - struct entry_set_cache_t *es = NULL; - struct buffer_head *tmp_bh = NULL; - struct super_block *sb = inode->i_sb; - struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info); - struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info); - - /* check the validity of the given file id */ - if (!fid) - return -EINVAL; - - /* check the validity of pointer parameters */ - if (!buffer) - return -EINVAL; - - /* acquire the lock for file system critical section */ - mutex_lock(&p_fs->v_mutex); - - /* check if the given file ID is opened */ - if (fid->type != TYPE_FILE) { - ret = -EPERM; - goto out; - } - - if (fid->rwoffset > fid->size) - fid->rwoffset = fid->size; - - if (count == 0) { - if (wcount) - *wcount = 0; - ret = 0; - goto out; - } - - fs_set_vol_flags(sb, VOL_DIRTY); - - if (fid->size == 0) - num_clusters = 0; - else - num_clusters = (s32)((fid->size - 1) >> - p_fs->cluster_size_bits) + 1; - - write_bytes = 0; - - while (count > 0) { - clu_offset = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); - clu = fid->start_clu; - last_clu = fid->start_clu; - - if (fid->flags == 0x03) { - if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { - last_clu += clu_offset - 1; - - if (clu_offset == num_clusters) - clu = CLUSTER_32(~0); - else - clu += clu_offset; - } - } else { - /* hint information */ - if ((clu_offset > 0) && (fid->hint_last_off > 0) && - (clu_offset >= fid->hint_last_off)) { - clu_offset -= fid->hint_last_off; - clu = fid->hint_last_clu; - } - - while ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { - last_clu = clu; - /* clu = exfat_fat_read(sb, clu); */ - if (exfat_fat_read(sb, clu, &clu) == -1) { - ret = -EIO; - goto out; - } - clu_offset--; - } - } - - if (clu == CLUSTER_32(~0)) { - num_alloc = (s32)((count - 1) >> - p_fs->cluster_size_bits) + 1; - new_clu.dir = (last_clu == CLUSTER_32(~0)) ? - CLUSTER_32(~0) : last_clu + 1; - new_clu.size = 0; - new_clu.flags = fid->flags; - - /* (1) allocate a chain of clusters */ - num_alloced = exfat_alloc_cluster(sb, - num_alloc, - &new_clu); - if (num_alloced == 0) - break; - if (num_alloced < 0) { - ret = num_alloced; - goto out; - } - - /* (2) append to the FAT chain */ - if (last_clu == CLUSTER_32(~0)) { - if (new_clu.flags == 0x01) - fid->flags = 0x01; - fid->start_clu = new_clu.dir; - modified = true; - } else { - if (new_clu.flags != fid->flags) { - exfat_chain_cont_cluster(sb, - fid->start_clu, - num_clusters); - fid->flags = 0x01; - modified = true; - } - if (new_clu.flags == 0x01) - exfat_fat_write(sb, last_clu, new_clu.dir); - } - - num_clusters += num_alloced; - clu = new_clu.dir; - } - - /* hint information */ - fid->hint_last_off = (s32)(fid->rwoffset >> - p_fs->cluster_size_bits); - fid->hint_last_clu = clu; - - /* byte offset in cluster */ - offset = (s32)(fid->rwoffset & (p_fs->cluster_size - 1)); - - /* sector offset in cluster */ - sec_offset = offset >> p_bd->sector_size_bits; - - /* byte offset in sector */ - offset &= p_bd->sector_size_mask; - - LogSector = START_SECTOR(clu) + sec_offset; - - oneblkwrite = (u64)(p_bd->sector_size - offset); - if (oneblkwrite > count) - oneblkwrite = count; - - if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) { - if (sector_read(sb, LogSector, &tmp_bh, 0) != - 0) - goto err_out; - memcpy((char *)tmp_bh->b_data, - (char *)buffer + write_bytes, (s32)oneblkwrite); - if (sector_write(sb, LogSector, tmp_bh, 0) != - 0) { - brelse(tmp_bh); - goto err_out; - } - } else { - if ((offset > 0) || - ((fid->rwoffset + oneblkwrite) < fid->size)) { - if (sector_read(sb, LogSector, &tmp_bh, 1) != - 0) - goto err_out; - } else { - if (sector_read(sb, LogSector, &tmp_bh, 0) != - 0) - goto err_out; - } - - memcpy((char *)tmp_bh->b_data + offset, - (char *)buffer + write_bytes, (s32)oneblkwrite); - if (sector_write(sb, LogSector, tmp_bh, 0) != - 0) { - brelse(tmp_bh); - goto err_out; - } - } - - count -= oneblkwrite; - write_bytes += oneblkwrite; - fid->rwoffset += oneblkwrite; - - fid->attr |= ATTR_ARCHIVE; - - if (fid->size < fid->rwoffset) { - fid->size = fid->rwoffset; - modified = true; - } - } - - brelse(tmp_bh); - - /* (3) update the direcoty entry */ - es = get_entry_set_in_dir(sb, &fid->dir, fid->entry, - ES_ALL_ENTRIES, &ep); - if (!es) - goto err_out; - ep2 = ep + 1; - - exfat_set_entry_time(ep, tm_current(&tm), TM_MODIFY); - exfat_set_entry_attr(ep, fid->attr); - - if (modified) { - if (exfat_get_entry_flag(ep2) != fid->flags) - exfat_set_entry_flag(ep2, fid->flags); - - if (exfat_get_entry_size(ep2) != fid->size) - exfat_set_entry_size(ep2, fid->size); - - if (exfat_get_entry_clu0(ep2) != fid->start_clu) - exfat_set_entry_clu0(ep2, fid->start_clu); - } - - update_dir_checksum_with_entry_set(sb, es); - release_entry_set(es); - -#ifndef CONFIG_STAGING_EXFAT_DELAYED_SYNC - fs_sync(sb, true); - fs_set_vol_flags(sb, VOL_CLEAN); -#endif - -err_out: - /* set the size of written bytes */ - if (wcount) - *wcount = write_bytes; - - if (num_alloced == 0) - ret = -ENOSPC; - - else if (p_fs->dev_ejected) - ret = -EIO; - -out: - /* release the lock for file system critical section */ - mutex_unlock(&p_fs->v_mutex); - - return ret; -} - static int ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) { s32 num_clusters; @@ -2273,7 +1904,6 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, int err; struct file_id_t fid; loff_t i_pos; - u64 ret; mode_t i_mode; __lock_super(sb); @@ -2295,18 +1925,6 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, } i_mode = inode->i_mode; - if (S_ISLNK(i_mode) && !EXFAT_I(inode)->target) { - EXFAT_I(inode)->target = kmalloc(i_size_read(inode) + 1, - GFP_KERNEL); - if (!EXFAT_I(inode)->target) { - err = -ENOMEM; - goto error; - } - ffsReadFile(dir, &fid, EXFAT_I(inode)->target, - i_size_read(inode), &ret); - *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; - } - alias = d_find_alias(inode); if (alias && !exfat_d_anon_disconn(alias)) { BUG_ON(d_unhashed(alias)); @@ -2398,73 +2016,6 @@ out: return err; } -static int exfat_symlink(struct inode *dir, struct dentry *dentry, - const char *target) -{ - struct super_block *sb = dir->i_sb; - struct timespec64 curtime; - struct inode *inode; - struct file_id_t fid; - loff_t i_pos; - int err; - u64 len = (u64)strlen(target); - u64 ret; - - __lock_super(sb); - - pr_debug("%s entered\n", __func__); - - err = ffsCreateFile(dir, (u8 *)dentry->d_name.name, FM_SYMLINK, &fid); - if (err) - goto out; - - - err = ffsWriteFile(dir, &fid, (char *)target, len, &ret); - - if (err) { - ffsRemoveFile(dir, &fid); - goto out; - } - - INC_IVERSION(dir); - curtime = current_time(dir); - dir->i_ctime = curtime; - dir->i_mtime = curtime; - dir->i_atime = curtime; - if (IS_DIRSYNC(dir)) - (void)exfat_sync_inode(dir); - else - mark_inode_dirty(dir); - - i_pos = ((loff_t)fid.dir.dir << 32) | (fid.entry & 0xffffffff); - - inode = exfat_build_inode(sb, &fid, i_pos); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); - goto out; - } - INC_IVERSION(inode); - curtime = current_time(inode); - inode->i_mtime = curtime; - inode->i_atime = curtime; - inode->i_ctime = curtime; - /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - - EXFAT_I(inode)->target = kmemdup(target, len + 1, GFP_KERNEL); - if (!EXFAT_I(inode)->target) { - err = -ENOMEM; - goto out; - } - - dentry->d_time = GET_IVERSION(dentry->d_parent->d_inode); - d_instantiate(dentry, inode); - -out: - __unlock_super(sb); - pr_debug("%s exited\n", __func__); - return err; -} - static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { struct super_block *sb = dir->i_sb; @@ -2838,7 +2389,6 @@ static const struct inode_operations exfat_dir_inode_operations = { .create = exfat_create, .lookup = exfat_lookup, .unlink = exfat_unlink, - .symlink = exfat_symlink, .mkdir = exfat_mkdir, .rmdir = exfat_rmdir, .rename = exfat_rename, From 86c468a4f8e649f7f1152aa89f883f7d5eb51aa3 Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Wed, 19 Feb 2020 21:47:38 +0530 Subject: [PATCH 0457/2231] staging: exfat: remove exfat_fat_sync() exfat_fat_sync() is not called anywhere, hence remove it from exfat_cache.c and exfat.h Acked-by: Valdis Kletnieks Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200219161738.GA22282@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 1 - drivers/staging/exfat/exfat_cache.c | 19 ------------------- 2 files changed, 20 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index c4ef6c2de329..e36d01b6fdc9 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -647,7 +647,6 @@ s32 exfat_fat_write(struct super_block *sb, u32 loc, u32 content); u8 *exfat_fat_getblk(struct super_block *sb, sector_t sec); void exfat_fat_modify(struct super_block *sb, sector_t sec); void exfat_fat_release_all(struct super_block *sb); -void exfat_fat_sync(struct super_block *sb); u8 *exfat_buf_getblk(struct super_block *sb, sector_t sec); void exfat_buf_modify(struct super_block *sb, sector_t sec); void exfat_buf_lock(struct super_block *sb, sector_t sec); diff --git a/drivers/staging/exfat/exfat_cache.c b/drivers/staging/exfat/exfat_cache.c index 3fd5604058a9..790ea4df9c00 100644 --- a/drivers/staging/exfat/exfat_cache.c +++ b/drivers/staging/exfat/exfat_cache.c @@ -341,25 +341,6 @@ void exfat_fat_release_all(struct super_block *sb) mutex_unlock(&f_mutex); } -void exfat_fat_sync(struct super_block *sb) -{ - struct buf_cache_t *bp; - struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info); - - mutex_lock(&f_mutex); - - bp = p_fs->FAT_cache_lru_list.next; - while (bp != &p_fs->FAT_cache_lru_list) { - if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { - sync_dirty_buffer(bp->buf_bh); - bp->flag &= ~(DIRTYBIT); - } - bp = bp->next; - } - - mutex_unlock(&f_mutex); -} - static struct buf_cache_t *buf_cache_find(struct super_block *sb, sector_t sec) { s32 off; From cf7e44daa727bf4868a4b3be380829db52a2762d Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sat, 22 Feb 2020 01:59:04 +0530 Subject: [PATCH 0458/2231] staging: qlge: add braces on all arms of if-else fix all checkpatch.pl warnings of 'braces {} should be used on all arms of this statement' in the file qlge_ethtool.c by adding the braces. Acked-by: Gustavo A. R. Silva Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200221202904.GA19627@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/qlge/qlge_ethtool.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/qlge/qlge_ethtool.c b/drivers/staging/qlge/qlge_ethtool.c index 790997aff995..592ca7edfc44 100644 --- a/drivers/staging/qlge/qlge_ethtool.c +++ b/drivers/staging/qlge/qlge_ethtool.c @@ -259,8 +259,9 @@ static void ql_update_stats(struct ql_adapter *qdev) "Error reading status register 0x%.04x.\n", i); goto end; - } else + } else { *iter = data; + } iter++; } @@ -273,8 +274,9 @@ static void ql_update_stats(struct ql_adapter *qdev) "Error reading status register 0x%.04x.\n", i); goto end; - } else + } else { *iter = data; + } iter++; } @@ -290,8 +292,9 @@ static void ql_update_stats(struct ql_adapter *qdev) "Error reading status register 0x%.04x.\n", i); goto end; - } else + } else { *iter = data; + } iter++; } @@ -304,8 +307,9 @@ static void ql_update_stats(struct ql_adapter *qdev) "Error reading status register 0x%.04x.\n", i); goto end; - } else + } else { *iter = data; + } iter++; } @@ -316,8 +320,9 @@ static void ql_update_stats(struct ql_adapter *qdev) netif_err(qdev, drv, qdev->ndev, "Error reading status register 0x%.04x.\n", i); goto end; - } else + } else { *iter = data; + } end: ql_sem_unlock(qdev, qdev->xg_sem_mask); quit: @@ -488,8 +493,9 @@ static int ql_start_loopback(struct ql_adapter *qdev) if (netif_carrier_ok(qdev->ndev)) { set_bit(QL_LB_LINK_UP, &qdev->flags); netif_carrier_off(qdev->ndev); - } else + } else { clear_bit(QL_LB_LINK_UP, &qdev->flags); + } qdev->link_config |= CFG_LOOPBACK_PCS; return ql_mb_set_port_cfg(qdev); } From 5979afa2c4d1c8acce28a176ebe27ea13265626a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 20 Feb 2020 07:29:08 -0600 Subject: [PATCH 0459/2231] staging: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200220132908.GA30501@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/staging/gdm724x/gdm_mux.h | 2 +- drivers/staging/gdm724x/hci_packet.h | 6 ++-- drivers/staging/greybus/audio_apbridgea.h | 2 +- drivers/staging/ks7010/ks_hostif.h | 4 +-- .../staging/media/allegro-dvt/allegro-core.c | 2 +- drivers/staging/octeon-usb/octeon-hcd.c | 2 +- drivers/staging/rtl8192e/rtllib.h | 30 +++++++++---------- .../staging/rtl8192u/ieee80211/ieee80211.h | 28 ++++++++--------- drivers/staging/rtl8712/ieee80211.h | 4 +-- drivers/staging/rtl8712/rtl871x_mp_ioctl.h | 4 +-- drivers/staging/wilc1000/cfg80211.c | 10 +++---- drivers/staging/wilc1000/spi.c | 2 +- drivers/staging/wlan-ng/hfa384x.h | 4 +-- drivers/staging/wlan-ng/p80211types.h | 4 +-- 14 files changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/staging/gdm724x/gdm_mux.h b/drivers/staging/gdm724x/gdm_mux.h index 51c22e3d8aeb..87b8d921fdc8 100644 --- a/drivers/staging/gdm724x/gdm_mux.h +++ b/drivers/staging/gdm724x/gdm_mux.h @@ -29,7 +29,7 @@ struct mux_pkt_header { __le32 seq_num; __le32 payload_size; __le16 packet_type; - unsigned char data[0]; + unsigned char data[]; }; struct mux_tx { diff --git a/drivers/staging/gdm724x/hci_packet.h b/drivers/staging/gdm724x/hci_packet.h index 6dea3694afdd..faecdfbc664f 100644 --- a/drivers/staging/gdm724x/hci_packet.h +++ b/drivers/staging/gdm724x/hci_packet.h @@ -28,7 +28,7 @@ struct hci_packet { __dev16 cmd_evt; __dev16 len; - u8 data[0]; + u8 data[]; } __packed; struct tlv { @@ -51,7 +51,7 @@ struct sdu { __dev32 dft_eps_ID; __dev32 bearer_ID; __dev32 nic_type; - u8 data[0]; + u8 data[]; } __packed; struct multi_sdu { @@ -59,7 +59,7 @@ struct multi_sdu { __dev16 len; __dev16 num_packet; __dev16 reserved; - u8 data[0]; + u8 data[]; } __packed; struct hci_pdn_table_ind { diff --git a/drivers/staging/greybus/audio_apbridgea.h b/drivers/staging/greybus/audio_apbridgea.h index 3f1f4dd2c61a..efec0f815efd 100644 --- a/drivers/staging/greybus/audio_apbridgea.h +++ b/drivers/staging/greybus/audio_apbridgea.h @@ -65,7 +65,7 @@ struct audio_apbridgea_hdr { __u8 type; __le16 i2s_port; - __u8 data[0]; + __u8 data[]; } __packed; struct audio_apbridgea_set_config_request { diff --git a/drivers/staging/ks7010/ks_hostif.h b/drivers/staging/ks7010/ks_hostif.h index ca7dc8f5166c..39138191a556 100644 --- a/drivers/staging/ks7010/ks_hostif.h +++ b/drivers/staging/ks7010/ks_hostif.h @@ -70,7 +70,7 @@ struct hostif_data_request { #define TYPE_DATA 0x0000 #define TYPE_AUTH 0x0001 __le16 reserved; - u8 data[0]; + u8 data[]; } __packed; #define TYPE_PMK1 0x0001 @@ -194,7 +194,7 @@ enum mib_data_type { struct hostif_mib_value { __le16 size; __le16 type; - u8 body[0]; + u8 body[]; } __packed; struct hostif_mib_get_confirm_t { diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 3be41698df4c..0a09b3622e78 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -434,7 +434,7 @@ struct mcu_msg_push_buffers_internal_buffer { struct mcu_msg_push_buffers_internal { struct mcu_msg_header header; u32 channel_id; - struct mcu_msg_push_buffers_internal_buffer buffer[0]; + struct mcu_msg_push_buffers_internal_buffer buffer[]; } __attribute__ ((__packed__)); struct mcu_msg_put_stream_buffer { diff --git a/drivers/staging/octeon-usb/octeon-hcd.c b/drivers/staging/octeon-usb/octeon-hcd.c index 582c9187559d..61471a19d4e6 100644 --- a/drivers/staging/octeon-usb/octeon-hcd.c +++ b/drivers/staging/octeon-usb/octeon-hcd.c @@ -406,7 +406,7 @@ struct octeon_hcd { */ struct octeon_temp_buffer { void *orig_buffer; - u8 data[0]; + u8 data[]; }; static inline struct usb_hcd *octeon_to_hcd(struct octeon_hcd *p) diff --git a/drivers/staging/rtl8192e/rtllib.h b/drivers/staging/rtl8192e/rtllib.h index 328f410daa03..b84f00b8d18b 100644 --- a/drivers/staging/rtl8192e/rtllib.h +++ b/drivers/staging/rtl8192e/rtllib.h @@ -728,14 +728,14 @@ struct rtllib_pspoll_hdr { struct rtllib_hdr { __le16 frame_ctl; __le16 duration_id; - u8 payload[0]; + u8 payload[]; } __packed; struct rtllib_hdr_1addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; - u8 payload[0]; + u8 payload[]; } __packed; struct rtllib_hdr_2addr { @@ -743,7 +743,7 @@ struct rtllib_hdr_2addr { __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; - u8 payload[0]; + u8 payload[]; } __packed; struct rtllib_hdr_3addr { @@ -753,7 +753,7 @@ struct rtllib_hdr_3addr { u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; __le16 seq_ctl; - u8 payload[0]; + u8 payload[]; } __packed; struct rtllib_hdr_4addr { @@ -764,7 +764,7 @@ struct rtllib_hdr_4addr { u8 addr3[ETH_ALEN]; __le16 seq_ctl; u8 addr4[ETH_ALEN]; - u8 payload[0]; + u8 payload[]; } __packed; struct rtllib_hdr_3addrqos { @@ -775,7 +775,7 @@ struct rtllib_hdr_3addrqos { u8 addr3[ETH_ALEN]; __le16 seq_ctl; __le16 qos_ctl; - u8 payload[0]; + u8 payload[]; } __packed; struct rtllib_hdr_4addrqos { @@ -787,13 +787,13 @@ struct rtllib_hdr_4addrqos { __le16 seq_ctl; u8 addr4[ETH_ALEN]; __le16 qos_ctl; - u8 payload[0]; + u8 payload[]; } __packed; struct rtllib_info_element { u8 id; u8 len; - u8 data[0]; + u8 data[]; } __packed; struct rtllib_authentication { @@ -802,7 +802,7 @@ struct rtllib_authentication { __le16 transaction; __le16 status; /*challenge*/ - struct rtllib_info_element info_element[0]; + struct rtllib_info_element info_element[]; } __packed; struct rtllib_disauth { @@ -818,7 +818,7 @@ struct rtllib_disassoc { struct rtllib_probe_request { struct rtllib_hdr_3addr header; /* SSID, supported rates */ - struct rtllib_info_element info_element[0]; + struct rtllib_info_element info_element[]; } __packed; struct rtllib_probe_response { @@ -829,7 +829,7 @@ struct rtllib_probe_response { /* SSID, supported rates, FH params, DS params, * CF params, IBSS params, TIM (if beacon), RSN */ - struct rtllib_info_element info_element[0]; + struct rtllib_info_element info_element[]; } __packed; /* Alias beacon for probe_response */ @@ -840,7 +840,7 @@ struct rtllib_assoc_request_frame { __le16 capability; __le16 listen_interval; /* SSID, supported rates, RSN */ - struct rtllib_info_element info_element[0]; + struct rtllib_info_element info_element[]; } __packed; struct rtllib_assoc_response_frame { @@ -848,7 +848,7 @@ struct rtllib_assoc_response_frame { __le16 capability; __le16 status; __le16 aid; - struct rtllib_info_element info_element[0]; /* supported rates */ + struct rtllib_info_element info_element[]; /* supported rates */ } __packed; struct rtllib_txb { @@ -859,7 +859,7 @@ struct rtllib_txb { u16 reserved; __le16 frag_size; __le16 payload_size; - struct sk_buff *fragments[0]; + struct sk_buff *fragments[]; }; #define MAX_SUBFRAME_COUNT 64 @@ -1792,7 +1792,7 @@ struct rtllib_device { /* This must be the last item so that it points to the data * allocated beyond this structure by alloc_rtllib */ - u8 priv[0]; + u8 priv[]; }; #define IEEE_A (1<<0) diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211.h b/drivers/staging/rtl8192u/ieee80211/ieee80211.h index 9576b647f6b1..39f4ddd86796 100644 --- a/drivers/staging/rtl8192u/ieee80211/ieee80211.h +++ b/drivers/staging/rtl8192u/ieee80211/ieee80211.h @@ -886,14 +886,14 @@ enum ieee80211_mfie { struct rtl_80211_hdr { __le16 frame_ctl; __le16 duration_id; - u8 payload[0]; + u8 payload[]; } __packed; struct rtl_80211_hdr_1addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; - u8 payload[0]; + u8 payload[]; } __packed; struct rtl_80211_hdr_2addr { @@ -901,7 +901,7 @@ struct rtl_80211_hdr_2addr { __le16 duration_id; u8 addr1[ETH_ALEN]; u8 addr2[ETH_ALEN]; - u8 payload[0]; + u8 payload[]; } __packed; struct rtl_80211_hdr_3addr { @@ -911,7 +911,7 @@ struct rtl_80211_hdr_3addr { u8 addr2[ETH_ALEN]; u8 addr3[ETH_ALEN]; __le16 seq_ctl; - u8 payload[0]; + u8 payload[]; } __packed; struct rtl_80211_hdr_4addr { @@ -922,7 +922,7 @@ struct rtl_80211_hdr_4addr { u8 addr3[ETH_ALEN]; __le16 seq_ctl; u8 addr4[ETH_ALEN]; - u8 payload[0]; + u8 payload[]; } __packed; struct rtl_80211_hdr_3addrqos { @@ -951,7 +951,7 @@ struct rtl_80211_hdr_4addrqos { struct ieee80211_info_element { u8 id; u8 len; - u8 data[0]; + u8 data[]; } __packed; struct ieee80211_authentication { @@ -960,7 +960,7 @@ struct ieee80211_authentication { __le16 transaction; __le16 status; /*challenge*/ - struct ieee80211_info_element info_element[0]; + struct ieee80211_info_element info_element[]; } __packed; struct ieee80211_disassoc { @@ -971,7 +971,7 @@ struct ieee80211_disassoc { struct ieee80211_probe_request { struct rtl_80211_hdr_3addr header; /* SSID, supported rates */ - struct ieee80211_info_element info_element[0]; + struct ieee80211_info_element info_element[]; } __packed; struct ieee80211_probe_response { @@ -982,7 +982,7 @@ struct ieee80211_probe_response { /* SSID, supported rates, FH params, DS params, * CF params, IBSS params, TIM (if beacon), RSN */ - struct ieee80211_info_element info_element[0]; + struct ieee80211_info_element info_element[]; } __packed; /* Alias beacon for probe_response */ @@ -993,7 +993,7 @@ struct ieee80211_assoc_request_frame { __le16 capability; __le16 listen_interval; /* SSID, supported rates, RSN */ - struct ieee80211_info_element info_element[0]; + struct ieee80211_info_element info_element[]; } __packed; struct ieee80211_reassoc_request_frame { @@ -1002,7 +1002,7 @@ struct ieee80211_reassoc_request_frame { __le16 listen_interval; u8 current_ap[ETH_ALEN]; /* SSID, supported rates, RSN */ - struct ieee80211_info_element info_element[0]; + struct ieee80211_info_element info_element[]; } __packed; struct ieee80211_assoc_response_frame { @@ -1010,7 +1010,7 @@ struct ieee80211_assoc_response_frame { __le16 capability; __le16 status; __le16 aid; - struct ieee80211_info_element info_element[0]; /* supported rates */ + struct ieee80211_info_element info_element[]; /* supported rates */ } __packed; struct ieee80211_txb { @@ -1021,7 +1021,7 @@ struct ieee80211_txb { u16 reserved; __le16 frag_size; __le16 payload_size; - struct sk_buff *fragments[0]; + struct sk_buff *fragments[]; }; #define MAX_TX_AGG_COUNT 16 @@ -2007,7 +2007,7 @@ struct ieee80211_device { /* This must be the last item so that it points to the data * allocated beyond this structure by alloc_ieee80211 */ - u8 priv[0]; + u8 priv[]; }; #define IEEE_A (1<<0) diff --git a/drivers/staging/rtl8712/ieee80211.h b/drivers/staging/rtl8712/ieee80211.h index 8098f6905554..dabaa8fd34fb 100644 --- a/drivers/staging/rtl8712/ieee80211.h +++ b/drivers/staging/rtl8712/ieee80211.h @@ -535,7 +535,7 @@ struct ieee80211_info_element_hdr { struct ieee80211_info_element { u8 id; u8 len; - u8 data[0]; + u8 data[]; } __packed; /* @@ -597,7 +597,7 @@ struct ieee80211_txb { u16 reserved; u16 frag_size; u16 payload_size; - struct sk_buff *fragments[0]; + struct sk_buff *fragments[]; }; /* SWEEP TABLE ENTRIES NUMBER*/ diff --git a/drivers/staging/rtl8712/rtl871x_mp_ioctl.h b/drivers/staging/rtl8712/rtl871x_mp_ioctl.h index 64e2ae436625..59fa6664d868 100644 --- a/drivers/staging/rtl8712/rtl871x_mp_ioctl.h +++ b/drivers/staging/rtl8712/rtl871x_mp_ioctl.h @@ -48,7 +48,7 @@ struct eeprom_rw_param { struct EFUSE_ACCESS_STRUCT { u16 start_addr; u16 cnts; - u8 data[0]; + u8 data[]; }; struct burst_rw_reg { @@ -324,7 +324,7 @@ struct mp_ioctl_handler { struct mp_ioctl_param { unsigned int subcode; unsigned int len; - unsigned char data[0]; + unsigned char data[]; }; #define GEN_MP_IOCTL_SUBCODE(code) _MP_IOCTL_ ## code ## _CMD_ diff --git a/drivers/staging/wilc1000/cfg80211.c b/drivers/staging/wilc1000/cfg80211.c index 995b1f306807..5d8faa01337d 100644 --- a/drivers/staging/wilc1000/cfg80211.c +++ b/drivers/staging/wilc1000/cfg80211.c @@ -62,7 +62,7 @@ struct wilc_p2p_pub_act_frame { u8 oui_type; u8 oui_subtype; u8 dialog_token; - u8 elem[0]; + u8 elem[]; } __packed; struct wilc_vendor_specific_ie { @@ -70,13 +70,13 @@ struct wilc_vendor_specific_ie { u8 tag_len; u8 oui[3]; u8 oui_type; - u8 attr[0]; + u8 attr[]; } __packed; struct wilc_attr_entry { u8 attr_type; __le16 attr_len; - u8 val[0]; + u8 val[]; } __packed; struct wilc_attr_oper_ch { @@ -91,13 +91,13 @@ struct wilc_attr_ch_list { u8 attr_type; __le16 attr_len; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - u8 elem[0]; + u8 elem[]; } __packed; struct wilc_ch_list_elem { u8 op_class; u8 no_of_channels; - u8 ch_list[0]; + u8 ch_list[]; } __packed; static void cfg_scan_result(enum scan_event scan_event, diff --git a/drivers/staging/wilc1000/spi.c b/drivers/staging/wilc1000/spi.c index 44f7d48851b5..11653ac118cd 100644 --- a/drivers/staging/wilc1000/spi.c +++ b/drivers/staging/wilc1000/spi.c @@ -139,7 +139,7 @@ struct wilc_spi_read_rsp_data { u8 status; u8 resp_header; u8 resp_data[4]; - u8 crc[0]; + u8 crc[]; } __packed; struct wilc_spi_rsp_data { diff --git a/drivers/staging/wlan-ng/hfa384x.h b/drivers/staging/wlan-ng/hfa384x.h index bdd7f414fdbb..88e894dd3568 100644 --- a/drivers/staging/wlan-ng/hfa384x.h +++ b/drivers/staging/wlan-ng/hfa384x.h @@ -355,7 +355,7 @@ /* Commonly used basic types */ struct hfa384x_bytestr { __le16 len; - u8 data[0]; + u8 data[]; } __packed; struct hfa384x_bytestr32 { @@ -421,7 +421,7 @@ struct hfa384x_authenticate_station_data { /*-- Configuration Record: WPAData (data portion only) --*/ struct hfa384x_wpa_data { __le16 datalen; - u8 data[0]; /* max 80 */ + u8 data[]; /* max 80 */ } __packed; /*-------------------------------------------------------------------- diff --git a/drivers/staging/wlan-ng/p80211types.h b/drivers/staging/wlan-ng/p80211types.h index ac254542fde6..3dcdd022da61 100644 --- a/drivers/staging/wlan-ng/p80211types.h +++ b/drivers/staging/wlan-ng/p80211types.h @@ -204,7 +204,7 @@ struct p80211pstr { struct p80211pstrd { u8 len; - u8 data[0]; + u8 data[]; } __packed; /* Maximum pascal string */ @@ -249,7 +249,7 @@ struct p80211itemd { u32 did; u16 status; u16 len; - u8 data[0]; + u8 data[]; } __packed; /* message data item for int, BOUNDEDINT, ENUMINT */ From 67e6cf7338e162d981b53c2432ebd48a465e694e Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sat, 22 Feb 2020 01:26:49 +0530 Subject: [PATCH 0460/2231] staging: qlge: add braces around macro arguments Fix checkpatch.pl warnings of adding braces around macro arguments to prevent precedence issues by adding braces in qlge_dbg.c Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200221195649.GA18450@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/qlge/qlge_dbg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/qlge/qlge_dbg.c b/drivers/staging/qlge/qlge_dbg.c index 8cf39615c520..c7af2548d119 100644 --- a/drivers/staging/qlge/qlge_dbg.c +++ b/drivers/staging/qlge/qlge_dbg.c @@ -1525,7 +1525,7 @@ void ql_dump_regs(struct ql_adapter *qdev) #ifdef QL_STAT_DUMP #define DUMP_STAT(qdev, stat) \ - pr_err("%s = %ld\n", #stat, (unsigned long)qdev->nic_stats.stat) + pr_err("%s = %ld\n", #stat, (unsigned long)(qdev)->nic_stats.stat) void ql_dump_stat(struct ql_adapter *qdev) { @@ -1578,12 +1578,12 @@ void ql_dump_stat(struct ql_adapter *qdev) #ifdef QL_DEV_DUMP #define DUMP_QDEV_FIELD(qdev, type, field) \ - pr_err("qdev->%-24s = " type "\n", #field, qdev->field) + pr_err("qdev->%-24s = " type "\n", #field, (qdev)->(field)) #define DUMP_QDEV_DMA_FIELD(qdev, field) \ pr_err("qdev->%-24s = %llx\n", #field, (unsigned long long)qdev->field) #define DUMP_QDEV_ARRAY(qdev, type, array, index, field) \ pr_err("%s[%d].%s = " type "\n", \ - #array, index, #field, qdev->array[index].field); + #array, index, #field, (qdev)->array[index].field); void ql_dump_qdev(struct ql_adapter *qdev) { int i; From 64e4965942e68526485e3d6a8eb7a0f716f42bf5 Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Sun, 23 Feb 2020 23:01:32 +0530 Subject: [PATCH 0461/2231] staging: qlge: emit debug and dump at same level Simplify code in ql_mpi_core_to_log() by calling print_hex_dump() instead of existing functions so that the debug and dump are emitted at the same KERN_ Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200223173132.GA13649@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/qlge/qlge_dbg.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/staging/qlge/qlge_dbg.c b/drivers/staging/qlge/qlge_dbg.c index c7af2548d119..44fb3a317b8d 100644 --- a/drivers/staging/qlge/qlge_dbg.c +++ b/drivers/staging/qlge/qlge_dbg.c @@ -1324,27 +1324,10 @@ void ql_mpi_core_to_log(struct work_struct *work) { struct ql_adapter *qdev = container_of(work, struct ql_adapter, mpi_core_to_log.work); - u32 *tmp, count; - int i; - count = sizeof(struct ql_mpi_coredump) / sizeof(u32); - tmp = (u32 *)qdev->mpi_coredump; - netif_printk(qdev, drv, KERN_DEBUG, qdev->ndev, - "Core is dumping to log file!\n"); - - for (i = 0; i < count; i += 8) { - pr_err("%.08x: %.08x %.08x %.08x %.08x %.08x " - "%.08x %.08x %.08x\n", i, - tmp[i + 0], - tmp[i + 1], - tmp[i + 2], - tmp[i + 3], - tmp[i + 4], - tmp[i + 5], - tmp[i + 6], - tmp[i + 7]); - msleep(5); - } + print_hex_dump(KERN_DEBUG, "Core is dumping to log file!\n", + DUMP_PREFIX_OFFSET, 32, 4, qdev->mpi_coredump, + sizeof(*qdev->mpi_coredump), false); } #ifdef QL_REG_DUMP From bc8942bc4910c7a146a1ed7531e0c2a310d2acaa Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Fri, 21 Feb 2020 01:18:20 +0530 Subject: [PATCH 0462/2231] staging: octeon: add space around '+' and parentheses Fix checkpatch.pl warnings of required spaces around '+' sign in multiple lines in octeon-stubs.h by adding spaces. Also add space before parentheses in the same file to fix checkpatch.pl warning. Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200220194820.GA13689@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/octeon-stubs.h | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index 79213c045504..d2bd379b1fd9 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -11,7 +11,7 @@ #define octeon_has_feature(x) 0 #define octeon_get_clock_rate() 0 -#define CVMX_SYNCIOBDMA do { } while(0) +#define CVMX_SYNCIOBDMA do { } while (0) #define CVMX_HELPER_INPUT_TAG_TYPE 0 #define CVMX_HELPER_FIRST_MBUFF_SKIP 7 @@ -22,11 +22,11 @@ #define CVMX_FPA_PACKET_POOL_SIZE 16 #define CVMX_FPA_WQE_POOL (1) #define CVMX_FPA_WQE_POOL_SIZE 16 -#define CVMX_GMXX_RXX_ADR_CAM_EN(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_ADR_CTL(a, b) ((a)+(b)) -#define CVMX_GMXX_PRTX_CFG(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_FRM_MAX(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_JABBER(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_ADR_CAM_EN(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_ADR_CTL(a, b) ((a) + (b)) +#define CVMX_GMXX_PRTX_CFG(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_FRM_MAX(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_JABBER(a, b) ((a) + (b)) #define CVMX_IPD_CTL_STATUS 0 #define CVMX_PIP_FRM_LEN_CHKX(a) (a) #define CVMX_PIP_NUM_INPUT_PORTS 1 @@ -1410,18 +1410,18 @@ static inline void cvmx_pow_work_submit(struct cvmx_wqe *wqp, uint32_t tag, uint64_t qos, uint64_t grp) { } -#define CVMX_ASXX_RX_CLK_SETX(a, b) ((a)+(b)) -#define CVMX_ASXX_TX_CLK_SETX(a, b) ((a)+(b)) +#define CVMX_ASXX_RX_CLK_SETX(a, b) ((a) + (b)) +#define CVMX_ASXX_TX_CLK_SETX(a, b) ((a) + (b)) #define CVMX_CIU_TIMX(a) (a) -#define CVMX_GMXX_RXX_ADR_CAM0(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_ADR_CAM1(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_ADR_CAM2(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_ADR_CAM3(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_ADR_CAM4(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_ADR_CAM5(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_FRM_CTL(a, b) ((a)+(b)) -#define CVMX_GMXX_RXX_INT_REG(a, b) ((a)+(b)) -#define CVMX_GMXX_SMACX(a, b) ((a)+(b)) +#define CVMX_GMXX_RXX_ADR_CAM0(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_ADR_CAM1(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_ADR_CAM2(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_ADR_CAM3(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_ADR_CAM4(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_ADR_CAM5(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_FRM_CTL(a, b) ((a) + (b)) +#define CVMX_GMXX_RXX_INT_REG(a, b) ((a) + (b)) +#define CVMX_GMXX_SMACX(a, b) ((a) + (b)) #define CVMX_PIP_PRT_TAGX(a) (a) #define CVMX_POW_PP_GRP_MSKX(a) (a) #define CVMX_POW_WQ_INT_THRX(a) (a) From 57dd99864f5c1a480662eabf763d1b5df5163150 Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Fri, 21 Feb 2020 01:26:54 +0530 Subject: [PATCH 0463/2231] staging: octeon: add blank line after union add a blank line after union declaration to fix checkpatch.pl warning Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200220195654.GA14056@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/octeon-stubs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index d2bd379b1fd9..ea33c94fa12b 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -382,6 +382,7 @@ union cvmx_ipd_sub_port_qos_cnt { uint64_t reserved_41_63:23; } s; }; + typedef struct { uint32_t dropped_octets; uint32_t dropped_packets; From 007b91435be85850017085f61061d2239468ca85 Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Fri, 21 Feb 2020 01:40:33 +0530 Subject: [PATCH 0464/2231] staging: octeon: match parentheses alignment match the next line with open parentheses by giving appropriate tabs. Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200220201033.GA14855@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/octeon-stubs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/octeon/octeon-stubs.h b/drivers/staging/octeon/octeon-stubs.h index ea33c94fa12b..d06743504f2b 100644 --- a/drivers/staging/octeon/octeon-stubs.h +++ b/drivers/staging/octeon/octeon-stubs.h @@ -1345,7 +1345,7 @@ static inline void cvmx_pow_work_request_async_nocheck(int scr_addr, { } static inline void cvmx_pow_work_request_async(int scr_addr, - cvmx_pow_wait_t wait) + cvmx_pow_wait_t wait) { } static inline struct cvmx_wqe *cvmx_pow_work_response_async(int scr_addr) From 6c411581caef6e3b2c286871641018364c6db50a Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 21 Feb 2020 11:30:20 +0000 Subject: [PATCH 0465/2231] staging: wilc1000: avoid double unlocking of 'wilc->hif_cs' mutex Possible double unlocking of 'wilc->hif_cs' mutex was identified by smatch [1]. Removed the extra call to release_bus() in wilc_wlan_handle_txq() which was missed in earlier commit fdc2ac1aafc6 ("staging: wilc1000: support suspend/resume functionality"). [1]. https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org/thread/NOEVW7C3GV74EWXJO3XX6VT2NKVB2HMT/ Reported-by: kbuild test robot Reported-by: Dan Carpenter Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200221170120.15739-1-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wilc1000/wlan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/wilc1000/wlan.c b/drivers/staging/wilc1000/wlan.c index f633c6b9f0a9..3aeca882f431 100644 --- a/drivers/staging/wilc1000/wlan.c +++ b/drivers/staging/wilc1000/wlan.c @@ -575,7 +575,6 @@ int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, reg); break; } - release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP); } while (--timeout); if (timeout <= 0) { ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0); From 31e234948e88aef0b2117b5c45ff432e38206b3a Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 21 Feb 2020 12:38:29 +0000 Subject: [PATCH 0466/2231] staging: wilc1000: use YAML schemas for DT binding documentation Use YAML schemas for wilc1000 DT binding documentations. Currently, the files are present in '/drivers/staging/wilc1000/' but these will be moved to '/Documentation/devicetree/bindings/net/wireless/' later once the driver move out-of-staging. Signed-off-by: Ajay Singh Link: https://lore.kernel.org/r/20200221123817.16643-1-ajay.kathat@microchip.com Signed-off-by: Greg Kroah-Hartman --- .../wilc1000/microchip,wilc1000,sdio.txt | 38 ----------- .../wilc1000/microchip,wilc1000,sdio.yaml | 68 +++++++++++++++++++ .../wilc1000/microchip,wilc1000,spi.txt | 34 ---------- .../wilc1000/microchip,wilc1000,spi.yaml | 61 +++++++++++++++++ 4 files changed, 129 insertions(+), 72 deletions(-) delete mode 100644 drivers/staging/wilc1000/microchip,wilc1000,sdio.txt create mode 100644 drivers/staging/wilc1000/microchip,wilc1000,sdio.yaml delete mode 100644 drivers/staging/wilc1000/microchip,wilc1000,spi.txt create mode 100644 drivers/staging/wilc1000/microchip,wilc1000,spi.yaml diff --git a/drivers/staging/wilc1000/microchip,wilc1000,sdio.txt b/drivers/staging/wilc1000/microchip,wilc1000,sdio.txt deleted file mode 100644 index da5235950a70..000000000000 --- a/drivers/staging/wilc1000/microchip,wilc1000,sdio.txt +++ /dev/null @@ -1,38 +0,0 @@ -* Microchip WILC wireless SDIO device - -The wilc1000 chips can be connected via SDIO. The node is used to specifiy -child node to the SDIO controller that connects the device to the system. - -Required properties: -- compatible : Should be "microchip,wilc1000-spi" -- irq-gpios : Connect to a host IRQ -- reg : Slot ID used in the controller - -Optional: -- bus-width : Number of data lines wired up the slot. Default 1 bit. -- rtc_clk : Clock connected on the rtc clock line. Must be assigned - a frequency with assigned-clocks property, and must be - connected to a clock provider. - -Examples: -mmc1: mmc@fc000000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_mmc1_clk_cmd_dat0 &pinctrl_mmc1_dat1_3>; - non-removable; - vmmc-supply = <&vcc_mmc1_reg>; - vqmmc-supply = <&vcc_3v3_reg>; - status = "okay"; - - wilc_sdio@0 { - compatible = "microchip,wilc1000-sdio"; - irq-gpios = <&pioC 27 0>; - clocks = <&pck1>; - clock-names = "rtc_clk"; - assigned-clocks = <&pck1>; - assigned-clock-rates = <32768>; - status = "okay"; - reg = <0>; - bus-width = <4>; - } - }; -} diff --git a/drivers/staging/wilc1000/microchip,wilc1000,sdio.yaml b/drivers/staging/wilc1000/microchip,wilc1000,sdio.yaml new file mode 100644 index 000000000000..b338f569f7e2 --- /dev/null +++ b/drivers/staging/wilc1000/microchip,wilc1000,sdio.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/microchip,wilc1000,sdio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip WILC wireless SDIO devicetree bindings + +maintainers: + - Adham Abozaeid + - Ajay Singh + +description: + The wilc1000 chips can be connected via SDIO. The node is used to + specify child node to the SDIO controller that connects the device + to the system. + +properties: + compatible: + const: microchip,wilc1000-sdio + + irq-gpios: + description: The GPIO phandle connect to a host IRQ. + maxItems: 1 + + reg: + description: Slot ID used in the controller. + maxItems: 1 + + clocks: + description: phandle to the clock connected on rtc clock line. + maxItems: 1 + + bus-width: + description: The number of data lines wired up the slot. + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + - enum: [1, 4, 8] + - default: 1 + +required: + - compatible + - irq-gpios + - reg + +examples: + - | + mmc1: mmc@fc000000 { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mmc1_clk_cmd_dat0 &pinctrl_mmc1_dat1_3>; + non-removable; + vmmc-supply = <&vcc_mmc1_reg>; + vqmmc-supply = <&vcc_3v3_reg>; + status = "okay"; + wilc_sdio@0 { + compatible = "microchip,wilc1000-sdio"; + irq-gpios = <&pioC 27 0>; + reg = <0>; + clocks = <&pck1>; + clock-names = "rtc_clk"; + assigned-clocks = <&pck1>; + assigned-clock-rates = <32768>; + status = "okay"; + bus-width = <4>; + }; + }; diff --git a/drivers/staging/wilc1000/microchip,wilc1000,spi.txt b/drivers/staging/wilc1000/microchip,wilc1000,spi.txt deleted file mode 100644 index 34236932dbb6..000000000000 --- a/drivers/staging/wilc1000/microchip,wilc1000,spi.txt +++ /dev/null @@ -1,34 +0,0 @@ -* Microchip WILC wireless SPI device - -The wilc1000 chips can be connected via SPI. This document describes -the binding for the SPI connected module. - -Required properties: -- compatible : Should be "microchip,wilc1000-spi" -- spi-max-frequency : Maximum SPI clocking speed of device in Hz -- reg : Chip select address of device -- irq-gpios : Connect to a host IRQ - -Optional: -- rtc_clk : Clock connected on the rtc clock line. Must be assigned - a frequency with assigned-clocks property, and must be - connected to a clock provider. - -Examples: - -spi1: spi@fc018000 { - cs-gpios = <&pioB 21 0>; - status = "okay"; - - wilc_spi@0 { - compatible = "microchip,wilc1000-spi"; - spi-max-frequency = <48000000>; - reg = <0>; - irq-gpios = <&pioC 27 0>; - clocks = <&pck1>; - clock-names = "rtc_clk"; - assigned-clocks = <&pck1>; - assigned-clock-rates = <32768>; - status = "okay"; - }; -}; diff --git a/drivers/staging/wilc1000/microchip,wilc1000,spi.yaml b/drivers/staging/wilc1000/microchip,wilc1000,spi.yaml new file mode 100644 index 000000000000..cc8ed64ce627 --- /dev/null +++ b/drivers/staging/wilc1000/microchip,wilc1000,spi.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/microchip,wilc1000,spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip WILC wireless SPI devicetree bindings + +maintainers: + - Adham Abozaeid + - Ajay Singh + +description: + The wilc1000 chips can be connected via SPI. This document describes + the binding for the SPI connected module. + +properties: + compatible: + const: microchip,wilc1000-spi + + spi-max-frequency: + description: Maximum SPI clocking speed of device in Hz. + maxItems: 1 + + reg: + description: Chip select address of device. + maxItems: 1 + + irq-gpios: + description: The GPIO phandle connect to a host IRQ. + maxItems: 1 + + clocks: + description: phandle to the clock connected on rtc clock line. + maxItems: 1 + +required: + - compatible + - spi-max-frequency + - reg + - irq-gpios + +examples: + - | + spi1: spi@fc018000 { + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&pioB 21 0>; + status = "okay"; + wilc_spi@0 { + compatible = "microchip,wilc1000-spi"; + spi-max-frequency = <48000000>; + reg = <0>; + irq-gpios = <&pioC 27 0>; + clocks = <&pck1>; + clock-names = "rtc_clk"; + assigned-clocks = <&pck1>; + assigned-clock-rates = <32768>; + status = "okay"; + }; + }; From df08620eab3bbef69010bf2b6b7736b69732dc21 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 22 Feb 2020 20:01:05 +0000 Subject: [PATCH 0467/2231] staging: rtl8192e: remove redundant initialization of variable init_status The pointer init_status is being initialized with a value that is never read, it is being updated later on. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200222200105.201869-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8192e/rtl8192e/rtl_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index ce57bf35c3ed..d3664e508cbe 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -732,7 +732,7 @@ static int _rtl92e_sta_up(struct net_device *dev, bool is_silent_reset) struct r8192_priv *priv = rtllib_priv(dev); struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *) (&priv->rtllib->PowerSaveControl); - bool init_status = true; + bool init_status; priv->bDriverIsGoingToUnload = false; priv->bdisable_nic = false; From 5a436cb13476133a1d0a9b0258da1e982796ce99 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 23 Feb 2020 15:14:38 +0000 Subject: [PATCH 0468/2231] staging: rtl8723bs: remove temporary variable CrystalCap Currently variable CrystalCap is being initialized with the value 0x20 that is never read so that is redundant and can be removed. Clean up the code by removing the need for variable CrystalCap since the calculation of the return value is relatively simple. Addresses-Coverity: ("Unused Value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200223151438.415542-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/hal/odm_CfoTracking.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c b/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c index 95edd148ac24..34d83b238265 100644 --- a/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c +++ b/drivers/staging/rtl8723bs/hal/odm_CfoTracking.c @@ -40,16 +40,11 @@ static void odm_SetCrystalCap(void *pDM_VOID, u8 CrystalCap) static u8 odm_GetDefaultCrytaltalCap(void *pDM_VOID) { PDM_ODM_T pDM_Odm = (PDM_ODM_T)pDM_VOID; - u8 CrystalCap = 0x20; struct adapter *Adapter = pDM_Odm->Adapter; struct hal_com_data *pHalData = GET_HAL_DATA(Adapter); - CrystalCap = pHalData->CrystalCap; - - CrystalCap = CrystalCap & 0x3f; - - return CrystalCap; + return pHalData->CrystalCap & 0x3f; } static void odm_SetATCStatus(void *pDM_VOID, bool ATCStatus) From 49ad571f70b36be713d73e16d20a1ba1765439e0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 23 Feb 2020 15:18:58 +0000 Subject: [PATCH 0469/2231] staging: rtl8188eu: remove redundant assignment to cond Variable cond is initialized to a value that is never read and it is re-assigned later. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200223151858.416499-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8188eu/hal/rf_cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/rtl8188eu/hal/rf_cfg.c b/drivers/staging/rtl8188eu/hal/rf_cfg.c index 47b1bf5a6143..004e19301eae 100644 --- a/drivers/staging/rtl8188eu/hal/rf_cfg.c +++ b/drivers/staging/rtl8188eu/hal/rf_cfg.c @@ -15,7 +15,7 @@ static bool check_condition(struct adapter *adapt, const u32 condition) u32 _board = odm->BoardType; u32 _platform = odm->SupportPlatform; u32 _interface = odm->SupportInterface; - u32 cond = condition; + u32 cond; if (condition == 0xCDCDCDCD) return true; From 999d544c600cb6528d3a1197d732e03058829204 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 23 Feb 2020 15:28:40 +0000 Subject: [PATCH 0470/2231] staging: rtl8723bs: core: remove redundant zero'ing of counter variable k The zero'ing of counter variable k is redundant as it is never read after breaking out of the while loop. Remove it. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200223152840.418439-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8723bs/core/rtw_efuse.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/rtl8723bs/core/rtw_efuse.c b/drivers/staging/rtl8723bs/core/rtw_efuse.c index 3b8848182221..bdb6ff8aab7d 100644 --- a/drivers/staging/rtl8723bs/core/rtw_efuse.c +++ b/drivers/staging/rtl8723bs/core/rtw_efuse.c @@ -244,10 +244,8 @@ u16 Address) while (!(Bytetemp & 0x80)) { Bytetemp = rtw_read8(Adapter, EFUSE_CTRL+3); k++; - if (k == 1000) { - k = 0; + if (k == 1000) break; - } } return rtw_read8(Adapter, EFUSE_CTRL); } else From ea3bc6acbf5c9f0ecb695895f7f2dbebe8544ed1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Feb 2020 13:21:31 +0100 Subject: [PATCH 0471/2231] staging: pi433: overlay: Fix Broadcom vendor prefix checkpatch.pl says: WARNING: DT compatible string "bcm,bcm2708" appears un-documented -- check ./Documentation/devicetree/bindings/ The vendor prefix of Broadcom Corporation is "brcm", not "bcm". Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200221122133.32024-2-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- .../staging/pi433/Documentation/devicetree/pi433-overlay.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts index 61fad96818c2..dfc9f974ac24 100644 --- a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts +++ b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts @@ -3,7 +3,7 @@ /plugin/; / { - compatible = "bcm,bcm2835", "bcm,bcm2708", "bcm,bcm2709"; + compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; fragment@0 { target = <&spi0>; From 43b83f1a4cb873f71fca084dcee49bfb8f31675e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Feb 2020 13:21:32 +0100 Subject: [PATCH 0472/2231] staging: pi433: overlay: Fix reg-related warnings When running "scripts/dtc/dtc -@ -I dts -O dtb -o pi433-overlay.dtbo.1 drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts": drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts:13.12-15.6: Warning (unit_address_vs_reg): /fragment@0/__overlay__/spidev@0: node has a unit name, but no reg property drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts:17.12-19.6: Warning (unit_address_vs_reg): /fragment@0/__overlay__/spidev@1: node has a unit name, but no reg property Add the missing "reg" properties to fix this. drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts:14.5-15: Warning (reg_format): /fragment@0/__overlay__/spidev@0:reg: property has invalid length (4 bytes) (#address-cells == 2, #size-cells == 1) drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts:19.5-15: Warning (reg_format): /fragment@0/__overlay__/spidev@1:reg: property has invalid length (4 bytes) (#address-cells == 2, #size-cells == 1) Add the missing "#{address,size}-cells" to fix this. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200221122133.32024-3-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- .../staging/pi433/Documentation/devicetree/pi433-overlay.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts index dfc9f974ac24..b584180d78d0 100644 --- a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts +++ b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts @@ -8,13 +8,17 @@ fragment@0 { target = <&spi0>; __overlay__ { + #address-cells = <1>; + #size-cells = <0>; status = "okay"; spidev@0{ + reg = <0>; status = "disabled"; }; spidev@1{ + reg = <1>; status = "disabled"; }; }; From 6d95783883634cc7632eb2131b7a2fd649278323 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Feb 2020 13:21:33 +0100 Subject: [PATCH 0473/2231] staging: pi433: overlay: Convert to sugar syntax Using overlay sugar syntax makes the DTS overlay files easier to read (and write). Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200221122133.32024-4-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- .../devicetree/pi433-overlay.dts | 81 +++++++++---------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts index b584180d78d0..096137fcd5cc 100644 --- a/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts +++ b/drivers/staging/pi433/Documentation/devicetree/pi433-overlay.dts @@ -4,54 +4,45 @@ / { compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; +}; - fragment@0 { - target = <&spi0>; - __overlay__ { - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; - spidev@0{ - reg = <0>; - status = "disabled"; - }; - - spidev@1{ - reg = <1>; - status = "disabled"; - }; - }; + spidev@0{ + reg = <0>; + status = "disabled"; }; - fragment@1 { - target = <&gpio>; - __overlay__ { - pi433_pins: pi433_pins { - brcm,pins = <7 25 24>; - brcm,function = <0 0 0>; // in in in - }; - }; - }; - - fragment@2 { - target = <&spi0>; - __overlay__ { - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; - - pi433: pi433@0 { - compatible = "Smarthome-Wolf,pi433"; - reg = <0>; - spi-max-frequency = <10000000>; - status = "okay"; - - pinctrl-0 = <&pi433_pins>; - DIO0-gpio = <&gpio 24 0>; - DIO1-gpio = <&gpio 25 0>; - DIO2-gpio = <&gpio 7 0>; - }; - }; + spidev@1{ + reg = <1>; + status = "disabled"; + }; +}; + +&gpio { + pi433_pins: pi433_pins { + brcm,pins = <7 25 24>; + brcm,function = <0 0 0>; // in in in + }; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + pi433: pi433@0 { + compatible = "Smarthome-Wolf,pi433"; + reg = <0>; + spi-max-frequency = <10000000>; + status = "okay"; + + pinctrl-0 = <&pi433_pins>; + DIO0-gpio = <&gpio 24 0>; + DIO1-gpio = <&gpio 25 0>; + DIO2-gpio = <&gpio 7 0>; }; }; From 42228d9edfa020cda6ebe83e9944acebc0389527 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 23 Feb 2020 15:39:54 +0000 Subject: [PATCH 0474/2231] staging: speakup: remove redundant initialization of pointer p_key Pointer p_key is being initialized with a value that is never read, it is assigned a new value later on. The initialization is redundant and can be removed. Reviewed-by: Samuel Thibault Signed-off-by: Colin Ian King Addresses-Coverity: ("Unused value") Link: https://lore.kernel.org/r/20200223153954.420731-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/speakup/keyhelp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/speakup/keyhelp.c b/drivers/staging/speakup/keyhelp.c index 5f1bda37f86d..822ceac83068 100644 --- a/drivers/staging/speakup/keyhelp.c +++ b/drivers/staging/speakup/keyhelp.c @@ -49,7 +49,7 @@ static int cur_item, nstates; static void build_key_data(void) { u_char *kp, counters[MAXFUNCS], ch, ch1; - u_short *p_key = key_data, key; + u_short *p_key, key; int i, offset = 1; nstates = (int)(state_tbl[-1]); From 6bc3f3979edce0b11deb685a4c817abb7d74b227 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 20 Feb 2020 07:20:17 -0600 Subject: [PATCH 0475/2231] USB: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200220132017.GA29262@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usbatm.h | 2 +- drivers/usb/dwc2/hcd.h | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ehci.h | 4 ++-- drivers/usb/host/fotg210.h | 2 +- drivers/usb/host/ohci.h | 4 ++-- drivers/usb/host/xhci-mtk.h | 2 +- drivers/usb/host/xhci.h | 4 ++-- drivers/usb/serial/io_usbvend.h | 4 ++-- drivers/usb/serial/ti_usb_3410_5052.c | 4 ++-- include/linux/usb.h | 4 ++-- include/linux/usb/audio-v2.h | 2 +- include/linux/usb/audio-v3.h | 2 +- include/linux/usb/gadget.h | 2 +- include/linux/usb/hcd.h | 2 +- include/linux/usbdevice_fs.h | 2 +- 16 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h index d3bdc4cc47aa..8725755bd53d 100644 --- a/drivers/usb/atm/usbatm.h +++ b/drivers/usb/atm/usbatm.h @@ -164,7 +164,7 @@ struct usbatm_data { unsigned char *cell_buf; /* holds partial rx cell */ unsigned int buf_usage; - struct urb *urbs[0]; + struct urb *urbs[]; }; static inline void *to_usbatm_driver_data(struct usb_interface *intf) diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 8ca6d12a6f57..1224fa9df604 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -199,7 +199,7 @@ struct dwc2_hcd_urb { u32 flags; u16 interval; struct dwc2_hcd_pipe_info pipe_info; - struct dwc2_hcd_iso_packet_desc iso_descs[0]; + struct dwc2_hcd_iso_packet_desc iso_descs[]; }; /* Phases for control transfers */ diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index d6433f206c17..10d51daa6a1b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -282,7 +282,7 @@ done: struct dma_aligned_buffer { void *kmalloc_ptr; void *old_xfer_buffer; - u8 data[0]; + u8 data[]; }; static void free_dma_aligned_buffer(struct urb *urb) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index ac5e967907d1..229b3de319e6 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -255,7 +255,7 @@ struct ehci_hcd { /* one per controller */ struct list_head tt_list; /* platform-specific data -- must come last */ - unsigned long priv[0] __aligned(sizeof(s64)); + unsigned long priv[] __aligned(sizeof(s64)); }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ @@ -460,7 +460,7 @@ struct ehci_iso_sched { struct list_head td_list; unsigned span; unsigned first_packet; - struct ehci_iso_packet packet[0]; + struct ehci_iso_packet packet[]; }; /* diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h index 1b4db95e5c43..6cee40ec65b4 100644 --- a/drivers/usb/host/fotg210.h +++ b/drivers/usb/host/fotg210.h @@ -490,7 +490,7 @@ struct fotg210_iso_packet { struct fotg210_iso_sched { struct list_head td_list; unsigned span; - struct fotg210_iso_packet packet[0]; + struct fotg210_iso_packet packet[]; }; /* diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index b015b00774b2..27c26ca10bfd 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -337,7 +337,7 @@ typedef struct urb_priv { u16 length; // # tds in this request u16 td_cnt; // tds already serviced struct list_head pending; - struct td *td [0]; // all TDs in this request + struct td *td[]; // all TDs in this request } urb_priv_t; @@ -435,7 +435,7 @@ struct ohci_hcd { struct dentry *debug_dir; /* platform-specific data -- must come last */ - unsigned long priv[0] __aligned(sizeof(s64)); + unsigned long priv[] __aligned(sizeof(s64)); }; diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 5ac458b7d2e0..acd56517215a 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -95,7 +95,7 @@ struct mu3h_sch_ep_info { u32 pkts; u32 cs_count; u32 burst_mode; - u32 bw_budget_table[0]; + u32 bw_budget_table[]; }; #define MU3C_U3_PORT_MAX 4 diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 13d8838cd552..5f47b18609ee 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1642,7 +1642,7 @@ struct xhci_scratchpad { struct urb_priv { int num_tds; int num_tds_done; - struct xhci_td td[0]; + struct xhci_td td[]; }; /* @@ -1893,7 +1893,7 @@ struct xhci_hcd { void *dbc; /* platform-specific data -- must come last */ - unsigned long priv[0] __aligned(sizeof(s64)); + unsigned long priv[] __aligned(sizeof(s64)); }; /* Platform specific overrides to generic XHCI hc_driver ops */ diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index c38e87ac5ea9..0d1a5bb4636e 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -593,7 +593,7 @@ struct ti_i2c_desc { __u8 Type; // Type of descriptor __le16 Size; // Size of data only not including header __u8 CheckSum; // Checksum (8 bit sum of data only) - __u8 Data[0]; // Data starts here + __u8 Data[]; // Data starts here } __attribute__((packed)); // for 5152 devices only (type 2 record) @@ -601,7 +601,7 @@ struct ti_i2c_desc { struct ti_i2c_firmware_rec { __u8 Ver_Major; // Firmware Major version number __u8 Ver_Minor; // Firmware Minor version number - __u8 Data[0]; // Download starts here + __u8 Data[]; // Download starts here } __attribute__((packed)); diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index ef23acc9b9ce..73075b9351c5 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -219,7 +219,7 @@ struct ti_write_data_bytes { u8 bDataCounter; __be16 wBaseAddrHi; __be16 wBaseAddrLo; - u8 bData[0]; + u8 bData[]; } __packed; struct ti_read_data_request { @@ -234,7 +234,7 @@ struct ti_read_data_bytes { __u8 bCmdCode; __u8 bModuleId; __u8 bErrorCode; - __u8 bData[0]; + __u8 bData[]; } __packed; /* Interrupt struct */ diff --git a/include/linux/usb.h b/include/linux/usb.h index ca1a5f1e1c5e..9f3c721c70dc 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -325,7 +325,7 @@ struct usb_interface_cache { /* variable-length array of alternate settings for this interface, * stored in no particular order */ - struct usb_host_interface altsetting[0]; + struct usb_host_interface altsetting[]; }; #define ref_to_usb_interface_cache(r) \ container_of(r, struct usb_interface_cache, ref) @@ -1589,7 +1589,7 @@ struct urb { int error_count; /* (return) number of ISO errors */ void *context; /* (in) context for completion */ usb_complete_t complete; /* (in) completion routine */ - struct usb_iso_packet_descriptor iso_frame_desc[0]; + struct usb_iso_packet_descriptor iso_frame_desc[]; /* (in) ISO ONLY */ }; diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index ba4b3e3327ff..5e31740c7e40 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -153,7 +153,7 @@ struct uac2_feature_unit_descriptor { __u8 bSourceID; /* bmaControls is actually u32, * but u8 is needed for the hybrid parser */ - __u8 bmaControls[0]; /* variable length */ + __u8 bmaControls[]; /* variable length */ } __attribute__((packed)); /* 4.9.2 Class-Specific AS Interface Descriptor */ diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h index 6b708434b7f9..c69a6f2e6837 100644 --- a/include/linux/usb/audio-v3.h +++ b/include/linux/usb/audio-v3.h @@ -109,7 +109,7 @@ struct uac3_feature_unit_descriptor { __u8 bSourceID; /* bmaControls is actually u32, * but u8 is needed for the hybrid parser */ - __u8 bmaControls[0]; /* variable length */ + __u8 bmaControls[]; /* variable length */ /* wFeatureDescrStr omitted */ } __attribute__((packed)); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 124462d65eac..9411c08a5c7e 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -767,7 +767,7 @@ struct usb_gadget_strings { struct usb_gadget_string_container { struct list_head list; - u8 *stash[0]; + u8 *stash[]; }; /* put descriptor for string with that id into buf (buflen >= 256) */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 712b2a603645..e12105ed3834 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -228,7 +228,7 @@ struct usb_hcd { /* The HC driver's private data is stored at the end of * this structure. */ - unsigned long hcd_priv[0] + unsigned long hcd_priv[] __attribute__ ((aligned(sizeof(s64)))); }; diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 79aab0065ec8..14ea197ce37f 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -69,7 +69,7 @@ struct usbdevfs_urb32 { compat_int_t error_count; compat_uint_t signr; compat_caddr_t usercontext; /* unused */ - struct usbdevfs_iso_packet_desc iso_frame_desc[0]; + struct usbdevfs_iso_packet_desc iso_frame_desc[]; }; struct usbdevfs_ioctl32 { From 52a39b075cdf8142a5153d1e2e3e27fc2d0b1606 Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Mon, 24 Feb 2020 00:53:47 +0530 Subject: [PATCH 0476/2231] staging: exfat: remove sync_alloc_bitmap() sync_alloc_bitmap() is not called anywhere, hence remove it from exfat_core.c and exfat.h Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200223192347.GA20286@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 1 - drivers/staging/exfat/exfat_core.c | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index e36d01b6fdc9..74f1abeda53a 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -666,7 +666,6 @@ void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len); /* allocation bitmap management functions */ s32 load_alloc_bitmap(struct super_block *sb); void free_alloc_bitmap(struct super_block *sb); -void sync_alloc_bitmap(struct super_block *sb); /* upcase table management functions */ s32 load_upcase_table(struct super_block *sb); diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c index 7308e50c0aaf..d30dc050411e 100644 --- a/drivers/staging/exfat/exfat_core.c +++ b/drivers/staging/exfat/exfat_core.c @@ -561,18 +561,6 @@ void free_alloc_bitmap(struct super_block *sb) p_fs->vol_amap = NULL; } -void sync_alloc_bitmap(struct super_block *sb) -{ - int i; - struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info); - - if (!p_fs->vol_amap) - return; - - for (i = 0; i < p_fs->map_sectors; i++) - sync_dirty_buffer(p_fs->vol_amap[i]); -} - /* * Upcase table Management Functions */ From f2409865c23ff83e08e07be8fa5d5d9b96f61487 Mon Sep 17 00:00:00 2001 From: Kaaira Gupta Date: Mon, 24 Feb 2020 00:46:23 +0530 Subject: [PATCH 0477/2231] staging: exfat: remove exfat_buf_sync() exfat_buf_sync() is not called anywhere, hence remove it from exfat_cache.c and exfat.h Signed-off-by: Kaaira Gupta Link: https://lore.kernel.org/r/20200223191623.GA20122@kaaira-HP-Pavilion-Notebook Signed-off-by: Greg Kroah-Hartman --- drivers/staging/exfat/exfat.h | 1 - drivers/staging/exfat/exfat_cache.c | 19 ------------------- 2 files changed, 20 deletions(-) diff --git a/drivers/staging/exfat/exfat.h b/drivers/staging/exfat/exfat.h index 74f1abeda53a..4a0a481fe010 100644 --- a/drivers/staging/exfat/exfat.h +++ b/drivers/staging/exfat/exfat.h @@ -653,7 +653,6 @@ void exfat_buf_lock(struct super_block *sb, sector_t sec); void exfat_buf_unlock(struct super_block *sb, sector_t sec); void exfat_buf_release(struct super_block *sb, sector_t sec); void exfat_buf_release_all(struct super_block *sb); -void exfat_buf_sync(struct super_block *sb); /* fs management functions */ void fs_set_vol_flags(struct super_block *sb, u32 new_flag); diff --git a/drivers/staging/exfat/exfat_cache.c b/drivers/staging/exfat/exfat_cache.c index 790ea4df9c00..87d019972050 100644 --- a/drivers/staging/exfat/exfat_cache.c +++ b/drivers/staging/exfat/exfat_cache.c @@ -515,22 +515,3 @@ void exfat_buf_release_all(struct super_block *sb) mutex_unlock(&b_mutex); } - -void exfat_buf_sync(struct super_block *sb) -{ - struct buf_cache_t *bp; - struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info); - - mutex_lock(&b_mutex); - - bp = p_fs->buf_cache_lru_list.next; - while (bp != &p_fs->buf_cache_lru_list) { - if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { - sync_dirty_buffer(bp->buf_bh); - bp->flag &= ~(DIRTYBIT); - } - bp = bp->next; - } - - mutex_unlock(&b_mutex); -} From 6f05106e20f0ed26a2dbd6a5d44a302ce81642e0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:32 +0100 Subject: [PATCH 0478/2231] efi/libstub: Use hidden visibility for all source files Instead of setting the visibility pragma for a small set of symbol declarations that could result in absolute references that we cannot support in the stub, declare hidden visibility for all code in the EFI stub, which is more robust and future proof. To ensure that the #pragma is taken into account before any other includes are processed, put it in a header file of its own and include it via the compiler command line using the -include option. Signed-off-by: Ard Biesheuvel --- arch/arm64/include/asm/efi.h | 3 --- drivers/firmware/efi/libstub/Makefile | 1 + drivers/firmware/efi/libstub/arm64-stub.c | 8 +------- drivers/firmware/efi/libstub/hidden.h | 6 ++++++ 4 files changed, 8 insertions(+), 10 deletions(-) create mode 100644 drivers/firmware/efi/libstub/hidden.h diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 44531a69d32b..56ae87401a26 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -107,9 +107,6 @@ static inline void free_screen_info(struct screen_info *si) { } -/* redeclare as 'hidden' so the compiler will generate relative references */ -extern struct screen_info screen_info __attribute__((__visibility__("hidden"))); - static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt) { } diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index f14b7636323a..fc9e9e883eb4 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -25,6 +25,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \ + -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \ -D__NO_FORTIFY \ $(call cc-option,-ffreestanding) \ $(call cc-option,-fno-stack-protector) \ diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index ad5f24a4489b..9254cd8ab2d3 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -6,17 +6,11 @@ * Adapted from ARM version by Mark Salter */ -/* - * To prevent the compiler from emitting GOT-indirected (and thus absolute) - * references to the section markers, override their visibility as 'hidden' - */ -#pragma GCC visibility push(hidden) -#include -#pragma GCC visibility pop #include #include #include +#include #include #include "efistub.h" diff --git a/drivers/firmware/efi/libstub/hidden.h b/drivers/firmware/efi/libstub/hidden.h new file mode 100644 index 000000000000..3493b041f419 --- /dev/null +++ b/drivers/firmware/efi/libstub/hidden.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * To prevent the compiler from emitting GOT-indirected (and thus absolute) + * references to any global symbols, override their visibility as 'hidden' + */ +#pragma GCC visibility push(hidden) From 184d7e0d7d6ea140fdc3c644b4c466505cfb1654 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:33 +0100 Subject: [PATCH 0479/2231] efi/libstub/arm: Relax FDT alignment requirement The arm64 kernel no longer requires the FDT blob to fit inside a naturally aligned 2 MB memory block, so remove the code that aligns the allocation to 2 MB. Signed-off-by: Ard Biesheuvel --- arch/arm64/include/asm/efi.h | 7 ------- drivers/firmware/efi/libstub/fdt.c | 6 +----- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 56ae87401a26..45e821222774 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -57,13 +57,6 @@ efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...); /* arch specific definitions used by the stub code */ -/* - * AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from - * start of kernel and may not cross a 2MiB boundary. We set alignment to - * 2MiB so we know it won't cross a 2MiB boundary. - */ -#define EFI_FDT_ALIGN SZ_2M /* used by allocate_new_fdt_and_exit_boot() */ - /* * In some configurations (e.g. VMAP_STACK && 64K pages), stacks built into the * kernel need greater alignment than we require the segments to be padded to. diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 0a91e5232127..f71cd54823b7 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -199,10 +199,6 @@ static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map) return EFI_SUCCESS; } -#ifndef EFI_FDT_ALIGN -# define EFI_FDT_ALIGN EFI_PAGE_SIZE -#endif - struct exit_boot_struct { efi_memory_desc_t *runtime_map; int *runtime_entry_count; @@ -281,7 +277,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, pr_efi("Exiting boot services and installing virtual address map...\n"); map.map = &memory_map; - status = efi_high_alloc(MAX_FDT_SIZE, EFI_FDT_ALIGN, + status = efi_high_alloc(MAX_FDT_SIZE, EFI_PAGE_SIZE, new_fdt_addr, max_addr); if (status != EFI_SUCCESS) { pr_efi_err("Unable to allocate memory for new device tree.\n"); From f57db62c67c1c9d3bf94563d3228ceff456f5be7 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:34 +0100 Subject: [PATCH 0480/2231] efi/libstub: Move memory map handling and allocation routines to mem.c Create a new source file mem.c to keep the routines involved in memory allocation and deallocation and manipulation of the EFI memory map. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 2 +- .../firmware/efi/libstub/efi-stub-helper.c | 313 ----------------- drivers/firmware/efi/libstub/mem.c | 319 ++++++++++++++++++ 3 files changed, 320 insertions(+), 314 deletions(-) create mode 100644 drivers/firmware/efi/libstub/mem.c diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index fc9e9e883eb4..7226d4b7175b 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -40,7 +40,7 @@ OBJECT_FILES_NON_STANDARD := y KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ - random.o pci.o + mem.o random.o pci.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 74ddfb496140..60d13c7a2e92 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -53,8 +53,6 @@ bool __pure __efi_soft_reserve_enabled(void) return !efi_nosoftreserve; } -#define EFI_MMAP_NR_SLACK_SLOTS 8 - struct file_info { efi_file_handle_t *handle; u64 size; @@ -77,64 +75,6 @@ void efi_printk(char *str) } } -static inline bool mmap_has_headroom(unsigned long buff_size, - unsigned long map_size, - unsigned long desc_size) -{ - unsigned long slack = buff_size - map_size; - - return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS; -} - -efi_status_t efi_get_memory_map(struct efi_boot_memmap *map) -{ - efi_memory_desc_t *m = NULL; - efi_status_t status; - unsigned long key; - u32 desc_version; - - *map->desc_size = sizeof(*m); - *map->map_size = *map->desc_size * 32; - *map->buff_size = *map->map_size; -again: - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, - *map->map_size, (void **)&m); - if (status != EFI_SUCCESS) - goto fail; - - *map->desc_size = 0; - key = 0; - status = efi_bs_call(get_memory_map, map->map_size, m, - &key, map->desc_size, &desc_version); - if (status == EFI_BUFFER_TOO_SMALL || - !mmap_has_headroom(*map->buff_size, *map->map_size, - *map->desc_size)) { - efi_bs_call(free_pool, m); - /* - * Make sure there is some entries of headroom so that the - * buffer can be reused for a new map after allocations are - * no longer permitted. Its unlikely that the map will grow to - * exceed this headroom once we are ready to trigger - * ExitBootServices() - */ - *map->map_size += *map->desc_size * EFI_MMAP_NR_SLACK_SLOTS; - *map->buff_size = *map->map_size; - goto again; - } - - if (status != EFI_SUCCESS) - efi_bs_call(free_pool, m); - - if (map->key_ptr && status == EFI_SUCCESS) - *map->key_ptr = key; - if (map->desc_ver && status == EFI_SUCCESS) - *map->desc_ver = desc_version; - -fail: - *map->map = m; - return status; -} - unsigned long get_dram_base(void) { @@ -170,192 +110,6 @@ unsigned long get_dram_base(void) return membase; } -/* - * Allocate at the highest possible address that is not above 'max'. - */ -efi_status_t efi_high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max) -{ - unsigned long map_size, desc_size, buff_size; - efi_memory_desc_t *map; - efi_status_t status; - unsigned long nr_pages; - u64 max_addr = 0; - int i; - struct efi_boot_memmap boot_map; - - boot_map.map = ↦ - boot_map.map_size = &map_size; - boot_map.desc_size = &desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - goto fail; - - /* - * Enforce minimum alignment that EFI or Linux requires when - * requesting a specific address. We are doing page-based (or - * larger) allocations, and both the address and size must meet - * alignment constraints. - */ - if (align < EFI_ALLOC_ALIGN) - align = EFI_ALLOC_ALIGN; - - size = round_up(size, EFI_ALLOC_ALIGN); - nr_pages = size / EFI_PAGE_SIZE; -again: - for (i = 0; i < map_size / desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map; - u64 start, end; - - desc = efi_early_memdesc_ptr(m, desc_size, i); - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (efi_soft_reserve_enabled() && - (desc->attribute & EFI_MEMORY_SP)) - continue; - - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * EFI_PAGE_SIZE; - - if (end > max) - end = max; - - if ((start + size) > end) - continue; - - if (round_down(end - size, align) < start) - continue; - - start = round_down(end - size, align); - - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. - */ - if (start == 0x0) - continue; - - if (start > max_addr) - max_addr = start; - } - - if (!max_addr) - status = EFI_NOT_FOUND; - else { - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, nr_pages, &max_addr); - if (status != EFI_SUCCESS) { - max = max_addr; - max_addr = 0; - goto again; - } - - *addr = max_addr; - } - - efi_bs_call(free_pool, map); -fail: - return status; -} - -/* - * Allocate at the lowest possible address that is not below 'min'. - */ -efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long min) -{ - unsigned long map_size, desc_size, buff_size; - efi_memory_desc_t *map; - efi_status_t status; - unsigned long nr_pages; - int i; - struct efi_boot_memmap boot_map; - - boot_map.map = ↦ - boot_map.map_size = &map_size; - boot_map.desc_size = &desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - goto fail; - - /* - * Enforce minimum alignment that EFI or Linux requires when - * requesting a specific address. We are doing page-based (or - * larger) allocations, and both the address and size must meet - * alignment constraints. - */ - if (align < EFI_ALLOC_ALIGN) - align = EFI_ALLOC_ALIGN; - - size = round_up(size, EFI_ALLOC_ALIGN); - nr_pages = size / EFI_PAGE_SIZE; - for (i = 0; i < map_size / desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map; - u64 start, end; - - desc = efi_early_memdesc_ptr(m, desc_size, i); - - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (efi_soft_reserve_enabled() && - (desc->attribute & EFI_MEMORY_SP)) - continue; - - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * EFI_PAGE_SIZE; - - if (start < min) - start = min; - - start = round_up(start, align); - if ((start + size) > end) - continue; - - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, nr_pages, &start); - if (status == EFI_SUCCESS) { - *addr = start; - break; - } - } - - if (i == map_size / desc_size) - status = EFI_NOT_FOUND; - - efi_bs_call(free_pool, map); -fail: - return status; -} - -void efi_free(unsigned long size, unsigned long addr) -{ - unsigned long nr_pages; - - if (!size) - return; - - nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; - efi_bs_call(free_pages, addr, nr_pages); -} - static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16, void **handle, u64 *file_sz) { @@ -695,73 +449,6 @@ fail: return status; } -/* - * Relocate a kernel image, either compressed or uncompressed. - * In the ARM64 case, all kernel images are currently - * uncompressed, and as such when we relocate it we need to - * allocate additional space for the BSS segment. Any low - * memory that this function should avoid needs to be - * unavailable in the EFI memory map, as if the preferred - * address is not available the lowest available address will - * be used. - */ -efi_status_t efi_relocate_kernel(unsigned long *image_addr, - unsigned long image_size, - unsigned long alloc_size, - unsigned long preferred_addr, - unsigned long alignment, - unsigned long min_addr) -{ - unsigned long cur_image_addr; - unsigned long new_addr = 0; - efi_status_t status; - unsigned long nr_pages; - efi_physical_addr_t efi_addr = preferred_addr; - - if (!image_addr || !image_size || !alloc_size) - return EFI_INVALID_PARAMETER; - if (alloc_size < image_size) - return EFI_INVALID_PARAMETER; - - cur_image_addr = *image_addr; - - /* - * The EFI firmware loader could have placed the kernel image - * anywhere in memory, but the kernel has restrictions on the - * max physical address it can run at. Some architectures - * also have a prefered address, so first try to relocate - * to the preferred address. If that fails, allocate as low - * as possible while respecting the required alignment. - */ - nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, nr_pages, &efi_addr); - new_addr = efi_addr; - /* - * If preferred address allocation failed allocate as low as - * possible. - */ - if (status != EFI_SUCCESS) { - status = efi_low_alloc_above(alloc_size, alignment, &new_addr, - min_addr); - } - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to allocate usable memory for kernel.\n"); - return status; - } - - /* - * We know source/dest won't overlap since both memory ranges - * have been allocated by UEFI, so we can safely use memcpy. - */ - memcpy((void *)new_addr, (void *)cur_image_addr, image_size); - - /* Return the new address of the relocated image. */ - *image_addr = new_addr; - - return status; -} - /* * Get the number of UTF-8 bytes corresponding to an UTF-16 character. * This overestimates for surrogates, but that is okay. diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c new file mode 100644 index 000000000000..690648a7ca1e --- /dev/null +++ b/drivers/firmware/efi/libstub/mem.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include "efistub.h" + +#define EFI_MMAP_NR_SLACK_SLOTS 8 + +static inline bool mmap_has_headroom(unsigned long buff_size, + unsigned long map_size, + unsigned long desc_size) +{ + unsigned long slack = buff_size - map_size; + + return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS; +} + +efi_status_t efi_get_memory_map(struct efi_boot_memmap *map) +{ + efi_memory_desc_t *m = NULL; + efi_status_t status; + unsigned long key; + u32 desc_version; + + *map->desc_size = sizeof(*m); + *map->map_size = *map->desc_size * 32; + *map->buff_size = *map->map_size; +again: + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, + *map->map_size, (void **)&m); + if (status != EFI_SUCCESS) + goto fail; + + *map->desc_size = 0; + key = 0; + status = efi_bs_call(get_memory_map, map->map_size, m, + &key, map->desc_size, &desc_version); + if (status == EFI_BUFFER_TOO_SMALL || + !mmap_has_headroom(*map->buff_size, *map->map_size, + *map->desc_size)) { + efi_bs_call(free_pool, m); + /* + * Make sure there is some entries of headroom so that the + * buffer can be reused for a new map after allocations are + * no longer permitted. Its unlikely that the map will grow to + * exceed this headroom once we are ready to trigger + * ExitBootServices() + */ + *map->map_size += *map->desc_size * EFI_MMAP_NR_SLACK_SLOTS; + *map->buff_size = *map->map_size; + goto again; + } + + if (status != EFI_SUCCESS) + efi_bs_call(free_pool, m); + + if (map->key_ptr && status == EFI_SUCCESS) + *map->key_ptr = key; + if (map->desc_ver && status == EFI_SUCCESS) + *map->desc_ver = desc_version; + +fail: + *map->map = m; + return status; +} + +/* + * Allocate at the highest possible address that is not above 'max'. + */ +efi_status_t efi_high_alloc(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long max) +{ + unsigned long map_size, desc_size, buff_size; + efi_memory_desc_t *map; + efi_status_t status; + unsigned long nr_pages; + u64 max_addr = 0; + int i; + struct efi_boot_memmap boot_map; + + boot_map.map = ↦ + boot_map.map_size = &map_size; + boot_map.desc_size = &desc_size; + boot_map.desc_ver = NULL; + boot_map.key_ptr = NULL; + boot_map.buff_size = &buff_size; + + status = efi_get_memory_map(&boot_map); + if (status != EFI_SUCCESS) + goto fail; + + /* + * Enforce minimum alignment that EFI or Linux requires when + * requesting a specific address. We are doing page-based (or + * larger) allocations, and both the address and size must meet + * alignment constraints. + */ + if (align < EFI_ALLOC_ALIGN) + align = EFI_ALLOC_ALIGN; + + size = round_up(size, EFI_ALLOC_ALIGN); + nr_pages = size / EFI_PAGE_SIZE; +again: + for (i = 0; i < map_size / desc_size; i++) { + efi_memory_desc_t *desc; + unsigned long m = (unsigned long)map; + u64 start, end; + + desc = efi_early_memdesc_ptr(m, desc_size, i); + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + if (efi_soft_reserve_enabled() && + (desc->attribute & EFI_MEMORY_SP)) + continue; + + if (desc->num_pages < nr_pages) + continue; + + start = desc->phys_addr; + end = start + desc->num_pages * EFI_PAGE_SIZE; + + if (end > max) + end = max; + + if ((start + size) > end) + continue; + + if (round_down(end - size, align) < start) + continue; + + start = round_down(end - size, align); + + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. + */ + if (start == 0x0) + continue; + + if (start > max_addr) + max_addr = start; + } + + if (!max_addr) + status = EFI_NOT_FOUND; + else { + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, nr_pages, &max_addr); + if (status != EFI_SUCCESS) { + max = max_addr; + max_addr = 0; + goto again; + } + + *addr = max_addr; + } + + efi_bs_call(free_pool, map); +fail: + return status; +} + +/* + * Allocate at the lowest possible address that is not below 'min'. + */ +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min) +{ + unsigned long map_size, desc_size, buff_size; + efi_memory_desc_t *map; + efi_status_t status; + unsigned long nr_pages; + int i; + struct efi_boot_memmap boot_map; + + boot_map.map = ↦ + boot_map.map_size = &map_size; + boot_map.desc_size = &desc_size; + boot_map.desc_ver = NULL; + boot_map.key_ptr = NULL; + boot_map.buff_size = &buff_size; + + status = efi_get_memory_map(&boot_map); + if (status != EFI_SUCCESS) + goto fail; + + /* + * Enforce minimum alignment that EFI or Linux requires when + * requesting a specific address. We are doing page-based (or + * larger) allocations, and both the address and size must meet + * alignment constraints. + */ + if (align < EFI_ALLOC_ALIGN) + align = EFI_ALLOC_ALIGN; + + size = round_up(size, EFI_ALLOC_ALIGN); + nr_pages = size / EFI_PAGE_SIZE; + for (i = 0; i < map_size / desc_size; i++) { + efi_memory_desc_t *desc; + unsigned long m = (unsigned long)map; + u64 start, end; + + desc = efi_early_memdesc_ptr(m, desc_size, i); + + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + if (efi_soft_reserve_enabled() && + (desc->attribute & EFI_MEMORY_SP)) + continue; + + if (desc->num_pages < nr_pages) + continue; + + start = desc->phys_addr; + end = start + desc->num_pages * EFI_PAGE_SIZE; + + if (start < min) + start = min; + + start = round_up(start, align); + if ((start + size) > end) + continue; + + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, nr_pages, &start); + if (status == EFI_SUCCESS) { + *addr = start; + break; + } + } + + if (i == map_size / desc_size) + status = EFI_NOT_FOUND; + + efi_bs_call(free_pool, map); +fail: + return status; +} + +void efi_free(unsigned long size, unsigned long addr) +{ + unsigned long nr_pages; + + if (!size) + return; + + nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; + efi_bs_call(free_pages, addr, nr_pages); +} + +/* + * Relocate a kernel image, either compressed or uncompressed. + * In the ARM64 case, all kernel images are currently + * uncompressed, and as such when we relocate it we need to + * allocate additional space for the BSS segment. Any low + * memory that this function should avoid needs to be + * unavailable in the EFI memory map, as if the preferred + * address is not available the lowest available address will + * be used. + */ +efi_status_t efi_relocate_kernel(unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment, + unsigned long min_addr) +{ + unsigned long cur_image_addr; + unsigned long new_addr = 0; + efi_status_t status; + unsigned long nr_pages; + efi_physical_addr_t efi_addr = preferred_addr; + + if (!image_addr || !image_size || !alloc_size) + return EFI_INVALID_PARAMETER; + if (alloc_size < image_size) + return EFI_INVALID_PARAMETER; + + cur_image_addr = *image_addr; + + /* + * The EFI firmware loader could have placed the kernel image + * anywhere in memory, but the kernel has restrictions on the + * max physical address it can run at. Some architectures + * also have a prefered address, so first try to relocate + * to the preferred address. If that fails, allocate as low + * as possible while respecting the required alignment. + */ + nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, nr_pages, &efi_addr); + new_addr = efi_addr; + /* + * If preferred address allocation failed allocate as low as + * possible. + */ + if (status != EFI_SUCCESS) { + status = efi_low_alloc_above(alloc_size, alignment, &new_addr, + min_addr); + } + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to allocate usable memory for kernel.\n"); + return status; + } + + /* + * We know source/dest won't overlap since both memory ranges + * have been allocated by UEFI, so we can safely use memcpy. + */ + memcpy((void *)new_addr, (void *)cur_image_addr, image_size); + + /* Return the new address of the relocated image. */ + *image_addr = new_addr; + + return status; +} From a7495c28c86ab3b31508c5754bc5fb717ab1169c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:35 +0100 Subject: [PATCH 0481/2231] efi/libstub: Simplify efi_high_alloc() and rename to efi_allocate_pages() The implementation of efi_high_alloc() uses a complicated way of traversing the memory map to find an available region that is located as close as possible to the provided upper limit, and calls AllocatePages subsequently to create the allocation at that exact address. This is precisely what the EFI_ALLOCATE_MAX_ADDRESS allocation type argument to AllocatePages() does, and considering that EFI_ALLOC_ALIGN only exceeds EFI_PAGE_SIZE on arm64, let's use AllocatePages() directly and implement the alignment using code that the compiler can remove if it does not exceed EFI_PAGE_SIZE. Signed-off-by: Ard Biesheuvel --- .../firmware/efi/libstub/efi-stub-helper.c | 5 +- drivers/firmware/efi/libstub/fdt.c | 3 +- drivers/firmware/efi/libstub/mem.c | 102 ++++-------------- include/linux/efi.h | 4 +- 4 files changed, 23 insertions(+), 91 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 60d13c7a2e92..7afe31357df3 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -385,8 +385,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, * so allocate enough memory for all the files. This is used * for loading multiple files. */ - status = efi_high_alloc(file_size_total, 0x1000, &file_addr, - max_addr); + status = efi_allocate_pages(file_size_total, &file_addr, max_addr); if (status != EFI_SUCCESS) { pr_efi_err("Failed to alloc highmem for files\n"); goto close_handles; @@ -536,7 +535,7 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, options_bytes++; /* NUL termination */ - status = efi_high_alloc(options_bytes, 0, &cmdline_addr, + status = efi_allocate_pages(options_bytes, &cmdline_addr, MAX_CMDLINE_ADDRESS); if (status != EFI_SUCCESS) return NULL; diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index f71cd54823b7..46cffac7a5f1 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -277,8 +277,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, pr_efi("Exiting boot services and installing virtual address map...\n"); map.map = &memory_map; - status = efi_high_alloc(MAX_FDT_SIZE, EFI_PAGE_SIZE, - new_fdt_addr, max_addr); + status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, max_addr); if (status != EFI_SUCCESS) { pr_efi_err("Unable to allocate memory for new device tree.\n"); goto fail; diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 690648a7ca1e..5808c8764e64 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -68,100 +68,34 @@ fail: /* * Allocate at the highest possible address that is not above 'max'. */ -efi_status_t efi_high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max) +efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, + unsigned long max) { - unsigned long map_size, desc_size, buff_size; - efi_memory_desc_t *map; + efi_physical_addr_t alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1; + int slack = EFI_ALLOC_ALIGN / EFI_PAGE_SIZE - 1; efi_status_t status; - unsigned long nr_pages; - u64 max_addr = 0; - int i; - struct efi_boot_memmap boot_map; - - boot_map.map = ↦ - boot_map.map_size = &map_size; - boot_map.desc_size = &desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - goto fail; - - /* - * Enforce minimum alignment that EFI or Linux requires when - * requesting a specific address. We are doing page-based (or - * larger) allocations, and both the address and size must meet - * alignment constraints. - */ - if (align < EFI_ALLOC_ALIGN) - align = EFI_ALLOC_ALIGN; size = round_up(size, EFI_ALLOC_ALIGN); - nr_pages = size / EFI_PAGE_SIZE; -again: - for (i = 0; i < map_size / desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map; - u64 start, end; + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, + EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack, + &alloc_addr); + if (status != EFI_SUCCESS) + return status; - desc = efi_early_memdesc_ptr(m, desc_size, i); - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; + *addr = ALIGN((unsigned long)alloc_addr, EFI_ALLOC_ALIGN); - if (efi_soft_reserve_enabled() && - (desc->attribute & EFI_MEMORY_SP)) - continue; + if (slack > 0) { + int l = (alloc_addr % EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * EFI_PAGE_SIZE; - - if (end > max) - end = max; - - if ((start + size) > end) - continue; - - if (round_down(end - size, align) < start) - continue; - - start = round_down(end - size, align); - - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. - */ - if (start == 0x0) - continue; - - if (start > max_addr) - max_addr = start; - } - - if (!max_addr) - status = EFI_NOT_FOUND; - else { - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, nr_pages, &max_addr); - if (status != EFI_SUCCESS) { - max = max_addr; - max_addr = 0; - goto again; + if (l) { + efi_bs_call(free_pages, alloc_addr, slack - l + 1); + slack = l - 1; } - - *addr = max_addr; + if (slack) + efi_bs_call(free_pages, *addr + size, slack); } - - efi_bs_call(free_pool, map); -fail: - return status; + return EFI_SUCCESS; } - /* * Allocate at the lowest possible address that is not below 'min'. */ diff --git a/include/linux/efi.h b/include/linux/efi.h index 7efd7072cca5..7e231c3cfb6f 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1508,8 +1508,8 @@ efi_status_t efi_low_alloc(unsigned long size, unsigned long align, return efi_low_alloc_above(size, align, addr, 0x8); } -efi_status_t efi_high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max); +efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, + unsigned long max); efi_status_t efi_relocate_kernel(unsigned long *image_addr, unsigned long image_size, From c2d0b470154c5be39f253da7814742030635f300 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:36 +0100 Subject: [PATCH 0482/2231] efi/libstub/x86: Incorporate eboot.c into libstub Most of the EFI stub source files of all architectures reside under drivers/firmware/efi/libstub, where they share a Makefile with special CFLAGS and an include file with declarations that are only relevant for stub code. Currently, we carry a lot of stub specific stuff in linux/efi.h only because eboot.c in arch/x86 needs them as well. So let's move eboot.c into libstub/, and move the contents of eboot.h that we still care about into efistub.h Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/Makefile | 5 +-- arch/x86/boot/compressed/eboot.h | 31 ------------------- drivers/firmware/efi/libstub/Makefile | 1 + drivers/firmware/efi/libstub/efistub.h | 16 ++++++++++ .../firmware/efi/libstub/x86-stub.c | 5 +-- 5 files changed, 19 insertions(+), 39 deletions(-) delete mode 100644 arch/x86/boot/compressed/eboot.h rename arch/x86/boot/compressed/eboot.c => drivers/firmware/efi/libstub/x86-stub.c (99%) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 26050ae0b27e..e51879bdc51c 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -87,10 +87,7 @@ endif vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o -$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone - -vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o \ - $(objtree)/drivers/firmware/efi/libstub/lib.a +vmlinux-objs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o # The compressed kernel is built with -fPIC/-fPIE so that a boot loader diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h deleted file mode 100644 index 99f35343d443..000000000000 --- a/arch/x86/boot/compressed/eboot.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef BOOT_COMPRESSED_EBOOT_H -#define BOOT_COMPRESSED_EBOOT_H - -#define SEG_TYPE_DATA (0 << 3) -#define SEG_TYPE_READ_WRITE (1 << 1) -#define SEG_TYPE_CODE (1 << 3) -#define SEG_TYPE_EXEC_READ (1 << 1) -#define SEG_TYPE_TSS ((1 << 3) | (1 << 0)) -#define SEG_OP_SIZE_32BIT (1 << 0) -#define SEG_GRANULARITY_4KB (1 << 0) - -#define DESC_TYPE_CODE_DATA (1 << 0) - -typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t; - -union efi_uga_draw_protocol { - struct { - efi_status_t (__efiapi *get_mode)(efi_uga_draw_protocol_t *, - u32*, u32*, u32*, u32*); - void *set_mode; - void *blt; - }; - struct { - u32 get_mode; - u32 set_mode; - u32 blt; - } mixed_mode; -}; - -#endif /* BOOT_COMPRESSED_EBOOT_H */ diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 7226d4b7175b..bda59d82b7f6 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -53,6 +53,7 @@ lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \ lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o +lib-$(CONFIG_X86) += x86-stub.o CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index c244b165005e..55de118e8267 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -90,4 +90,20 @@ void *get_efi_config_table(efi_guid_t guid); efi_rt_call(set_variable, (efi_char16_t *)(name), \ (efi_guid_t *)(vendor), __VA_ARGS__) +typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t; + +union efi_uga_draw_protocol { + struct { + efi_status_t (__efiapi *get_mode)(efi_uga_draw_protocol_t *, + u32*, u32*, u32*, u32*); + void *set_mode; + void *blt; + }; + struct { + u32 get_mode; + u32 set_mode; + u32 blt; + } mixed_mode; +}; + #endif diff --git a/arch/x86/boot/compressed/eboot.c b/drivers/firmware/efi/libstub/x86-stub.c similarity index 99% rename from arch/x86/boot/compressed/eboot.c rename to drivers/firmware/efi/libstub/x86-stub.c index 55637135b50c..7e7c50883cce 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -6,8 +6,6 @@ * * ----------------------------------------------------------------------- */ -#pragma GCC visibility push(hidden) - #include #include @@ -17,8 +15,7 @@ #include #include -#include "../string.h" -#include "eboot.h" +#include "efistub.h" static efi_system_table_t *sys_table; extern const bool efi_is64; From a46a290a01149120f40f83a694d3e6041bcf8f70 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:37 +0100 Subject: [PATCH 0483/2231] efi/libstub: Use consistent type names for file I/O protocols Align the naming of efi_file_io_interface_t and efi_file_handle_t with the UEFI spec, and call them efi_simple_file_system_protocol_t and efi_file_protocol_t, respectively, using the same convention we use for all other type definitions that originate in the UEFI spec. While at it, move the definitions to efistub.h, so they are only seen by code that needs them. Signed-off-by: Ard Biesheuvel --- .../firmware/efi/libstub/efi-stub-helper.c | 16 ++--- drivers/firmware/efi/libstub/efistub.h | 63 +++++++++++++++++++ include/linux/efi.h | 60 +----------------- 3 files changed, 72 insertions(+), 67 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 7afe31357df3..6db91655c743 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -54,7 +54,7 @@ bool __pure __efi_soft_reserve_enabled(void) } struct file_info { - efi_file_handle_t *handle; + efi_file_protocol_t *handle; u64 size; }; @@ -113,7 +113,7 @@ unsigned long get_dram_base(void) static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16, void **handle, u64 *file_sz) { - efi_file_handle_t *h, *fh = __fh; + efi_file_protocol_t *h, *fh = __fh; efi_file_info_t *info; efi_status_t status; efi_guid_t info_guid = EFI_FILE_INFO_ID; @@ -159,22 +159,22 @@ grow: return status; } -static efi_status_t efi_file_read(efi_file_handle_t *handle, +static efi_status_t efi_file_read(efi_file_protocol_t *handle, unsigned long *size, void *addr) { return handle->read(handle, size, addr); } -static efi_status_t efi_file_close(efi_file_handle_t *handle) +static efi_status_t efi_file_close(efi_file_protocol_t *handle) { return handle->close(handle); } static efi_status_t efi_open_volume(efi_loaded_image_t *image, - efi_file_handle_t **__fh) + efi_file_protocol_t **__fh) { - efi_file_io_interface_t *io; - efi_file_handle_t *fh; + efi_simple_file_system_protocol_t *io; + efi_file_protocol_t *fh; efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; efi_status_t status; efi_handle_t handle = image->device_handle; @@ -282,7 +282,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, struct file_info *files; unsigned long file_addr; u64 file_size_total; - efi_file_handle_t *fh = NULL; + efi_file_protocol_t *fh = NULL; efi_status_t status; int nr_files; char *str; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 55de118e8267..79cdb219f439 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -106,4 +106,67 @@ union efi_uga_draw_protocol { } mixed_mode; }; +typedef struct efi_loaded_image { + u32 revision; + efi_handle_t parent_handle; + efi_system_table_t *system_table; + efi_handle_t device_handle; + void *file_path; + void *reserved; + u32 load_options_size; + void *load_options; + void *image_base; + __aligned_u64 image_size; + unsigned int image_code_type; + unsigned int image_data_type; + efi_status_t (__efiapi *unload)(efi_handle_t image_handle); +} efi_loaded_image_t; + +typedef struct { + u64 size; + u64 file_size; + u64 phys_size; + efi_time_t create_time; + efi_time_t last_access_time; + efi_time_t modification_time; + __aligned_u64 attribute; + efi_char16_t filename[1]; +} efi_file_info_t; + +typedef struct efi_file_protocol efi_file_protocol_t; + +struct efi_file_protocol { + u64 revision; + efi_status_t (__efiapi *open) (efi_file_protocol_t *, + efi_file_protocol_t **, + efi_char16_t *, u64, u64); + efi_status_t (__efiapi *close) (efi_file_protocol_t *); + efi_status_t (__efiapi *delete) (efi_file_protocol_t *); + efi_status_t (__efiapi *read) (efi_file_protocol_t *, + unsigned long *, void *); + efi_status_t (__efiapi *write) (efi_file_protocol_t *, + unsigned long, void *); + efi_status_t (__efiapi *get_position)(efi_file_protocol_t *, u64 *); + efi_status_t (__efiapi *set_position)(efi_file_protocol_t *, u64); + efi_status_t (__efiapi *get_info) (efi_file_protocol_t *, + efi_guid_t *, unsigned long *, + void *); + efi_status_t (__efiapi *set_info) (efi_file_protocol_t *, + efi_guid_t *, unsigned long, + void *); + efi_status_t (__efiapi *flush) (efi_file_protocol_t *); +}; + +typedef struct efi_simple_file_system_protocol efi_simple_file_system_protocol_t; + +struct efi_simple_file_system_protocol { + u64 revision; + int (__efiapi *open_volume)(efi_simple_file_system_protocol_t *, + efi_file_protocol_t **); +}; + +#define EFI_FILE_MODE_READ 0x0000000000000001 +#define EFI_FILE_MODE_WRITE 0x0000000000000002 +#define EFI_FILE_MODE_CREATE 0x8000000000000000 + #endif diff --git a/include/linux/efi.h b/include/linux/efi.h index 7e231c3cfb6f..2b228df18407 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -796,65 +796,7 @@ struct efi_fdt_params { u32 desc_ver; }; -typedef struct { - u32 revision; - efi_handle_t parent_handle; - efi_system_table_t *system_table; - efi_handle_t device_handle; - void *file_path; - void *reserved; - u32 load_options_size; - void *load_options; - void *image_base; - __aligned_u64 image_size; - unsigned int image_code_type; - unsigned int image_data_type; - efi_status_t ( __efiapi *unload)(efi_handle_t image_handle); -} efi_loaded_image_t; - -typedef struct { - u64 size; - u64 file_size; - u64 phys_size; - efi_time_t create_time; - efi_time_t last_access_time; - efi_time_t modification_time; - __aligned_u64 attribute; - efi_char16_t filename[1]; -} efi_file_info_t; - -typedef struct efi_file_handle efi_file_handle_t; - -struct efi_file_handle { - u64 revision; - efi_status_t (__efiapi *open)(efi_file_handle_t *, - efi_file_handle_t **, - efi_char16_t *, u64, u64); - efi_status_t (__efiapi *close)(efi_file_handle_t *); - void *delete; - efi_status_t (__efiapi *read)(efi_file_handle_t *, - unsigned long *, void *); - void *write; - void *get_position; - void *set_position; - efi_status_t (__efiapi *get_info)(efi_file_handle_t *, - efi_guid_t *, unsigned long *, - void *); - void *set_info; - void *flush; -}; - -typedef struct efi_file_io_interface efi_file_io_interface_t; - -struct efi_file_io_interface { - u64 revision; - int (__efiapi *open_volume)(efi_file_io_interface_t *, - efi_file_handle_t **); -}; - -#define EFI_FILE_MODE_READ 0x0000000000000001 -#define EFI_FILE_MODE_WRITE 0x0000000000000002 -#define EFI_FILE_MODE_CREATE 0x8000000000000000 +typedef struct efi_loaded_image efi_loaded_image_t; typedef struct { u32 version; From 6a4db9bfdd56ede30686d8e1cfadedc8b22a6bf5 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:39 +0100 Subject: [PATCH 0484/2231] efi/libstub/x86: Permit bootparams struct to be allocated above 4 GB We now support bootparams structures that are located in memory that is not 32-bit addressable, so relax the allocation limit on systems where this feature is enabled. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/x86-stub.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 7e7c50883cce..9d60352baa0f 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -363,6 +363,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, char *cmdline_ptr; unsigned long ramdisk_addr; unsigned long ramdisk_size; + bool above4g; sys_table = sys_table_arg; @@ -376,7 +377,11 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, return status; } - status = efi_low_alloc(0x4000, 1, (unsigned long *)&boot_params); + hdr = &((struct boot_params *)image->image_base)->hdr; + above4g = hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G; + + status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, + above4g ? ULONG_MAX : UINT_MAX); if (status != EFI_SUCCESS) { efi_printk("Failed to allocate lowmem for boot params\n"); return status; From 8166ec091573fad528d884ede291fd1ec02d0298 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:38 +0100 Subject: [PATCH 0485/2231] efi/libstub: Move stub specific declarations into efistub.h Move all the declarations that are only used in stub code from linux/efi.h to efistub.h which is only included locally. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efistub.h | 507 +++++++++++++++++++++++-- include/linux/efi.h | 456 +--------------------- 2 files changed, 479 insertions(+), 484 deletions(-) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 79cdb219f439..8bb46c1fd2cd 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -43,34 +43,6 @@ extern __pure efi_system_table_t *efi_system_table(void); #define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg) -void efi_char16_printk(efi_char16_t *); -void efi_char16_printk(efi_char16_t *); - -unsigned long get_dram_base(void); - -efi_status_t allocate_new_fdt_and_exit_boot(void *handle, - unsigned long *new_fdt_addr, - unsigned long max_addr, - u64 initrd_addr, u64 initrd_size, - char *cmdline_ptr, - unsigned long fdt_addr, - unsigned long fdt_size); - -void *get_fdt(unsigned long *fdt_size); - -void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, - unsigned long desc_size, efi_memory_desc_t *runtime_map, - int *count); - -efi_status_t efi_get_random_bytes(unsigned long size, u8 *out); - -efi_status_t efi_random_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long random_seed); - -efi_status_t check_platform_features(void); - -void *get_efi_config_table(efi_guid_t guid); - /* Helper macros for the usual case of using simple C variables: */ #ifndef fdt_setprop_inplace_var #define fdt_setprop_inplace_var(fdt, node_offset, name, var) \ @@ -90,6 +62,156 @@ void *get_efi_config_table(efi_guid_t guid); efi_rt_call(set_variable, (efi_char16_t *)(name), \ (efi_guid_t *)(vendor), __VA_ARGS__) +#define efi_get_handle_at(array, idx) \ + (efi_is_native() ? (array)[idx] \ + : (efi_handle_t)(unsigned long)((u32 *)(array))[idx]) + +#define efi_get_handle_num(size) \ + ((size) / (efi_is_native() ? sizeof(efi_handle_t) : sizeof(u32))) + +#define for_each_efi_handle(handle, array, size, i) \ + for (i = 0; \ + i < efi_get_handle_num(size) && \ + ((handle = efi_get_handle_at((array), i)) || true); \ + i++) + +/* + * Allocation types for calls to boottime->allocate_pages. + */ +#define EFI_ALLOCATE_ANY_PAGES 0 +#define EFI_ALLOCATE_MAX_ADDRESS 1 +#define EFI_ALLOCATE_ADDRESS 2 +#define EFI_MAX_ALLOCATE_TYPE 3 + +/* + * The type of search to perform when calling boottime->locate_handle + */ +#define EFI_LOCATE_ALL_HANDLES 0 +#define EFI_LOCATE_BY_REGISTER_NOTIFY 1 +#define EFI_LOCATE_BY_PROTOCOL 2 + +struct efi_boot_memmap { + efi_memory_desc_t **map; + unsigned long *map_size; + unsigned long *desc_size; + u32 *desc_ver; + unsigned long *key_ptr; + unsigned long *buff_size; +}; + +/* + * EFI Boot Services table + */ +union efi_boot_services { + struct { + efi_table_hdr_t hdr; + void *raise_tpl; + void *restore_tpl; + efi_status_t (__efiapi *allocate_pages)(int, int, unsigned long, + efi_physical_addr_t *); + efi_status_t (__efiapi *free_pages)(efi_physical_addr_t, + unsigned long); + efi_status_t (__efiapi *get_memory_map)(unsigned long *, void *, + unsigned long *, + unsigned long *, u32 *); + efi_status_t (__efiapi *allocate_pool)(int, unsigned long, + void **); + efi_status_t (__efiapi *free_pool)(void *); + void *create_event; + void *set_timer; + void *wait_for_event; + void *signal_event; + void *close_event; + void *check_event; + void *install_protocol_interface; + void *reinstall_protocol_interface; + void *uninstall_protocol_interface; + efi_status_t (__efiapi *handle_protocol)(efi_handle_t, + efi_guid_t *, void **); + void *__reserved; + void *register_protocol_notify; + efi_status_t (__efiapi *locate_handle)(int, efi_guid_t *, + void *, unsigned long *, + efi_handle_t *); + void *locate_device_path; + efi_status_t (__efiapi *install_configuration_table)(efi_guid_t *, + void *); + void *load_image; + void *start_image; + void *exit; + void *unload_image; + efi_status_t (__efiapi *exit_boot_services)(efi_handle_t, + unsigned long); + void *get_next_monotonic_count; + void *stall; + void *set_watchdog_timer; + void *connect_controller; + efi_status_t (__efiapi *disconnect_controller)(efi_handle_t, + efi_handle_t, + efi_handle_t); + void *open_protocol; + void *close_protocol; + void *open_protocol_information; + void *protocols_per_handle; + void *locate_handle_buffer; + efi_status_t (__efiapi *locate_protocol)(efi_guid_t *, void *, + void **); + void *install_multiple_protocol_interfaces; + void *uninstall_multiple_protocol_interfaces; + void *calculate_crc32; + void *copy_mem; + void *set_mem; + void *create_event_ex; + }; + struct { + efi_table_hdr_t hdr; + u32 raise_tpl; + u32 restore_tpl; + u32 allocate_pages; + u32 free_pages; + u32 get_memory_map; + u32 allocate_pool; + u32 free_pool; + u32 create_event; + u32 set_timer; + u32 wait_for_event; + u32 signal_event; + u32 close_event; + u32 check_event; + u32 install_protocol_interface; + u32 reinstall_protocol_interface; + u32 uninstall_protocol_interface; + u32 handle_protocol; + u32 __reserved; + u32 register_protocol_notify; + u32 locate_handle; + u32 locate_device_path; + u32 install_configuration_table; + u32 load_image; + u32 start_image; + u32 exit; + u32 unload_image; + u32 exit_boot_services; + u32 get_next_monotonic_count; + u32 stall; + u32 set_watchdog_timer; + u32 connect_controller; + u32 disconnect_controller; + u32 open_protocol; + u32 close_protocol; + u32 open_protocol_information; + u32 protocols_per_handle; + u32 locate_handle_buffer; + u32 locate_protocol; + u32 install_multiple_protocol_interfaces; + u32 uninstall_multiple_protocol_interfaces; + u32 calculate_crc32; + u32 copy_mem; + u32 set_mem; + u32 create_event_ex; + } mixed_mode; +}; + typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t; union efi_uga_draw_protocol { @@ -106,7 +228,81 @@ union efi_uga_draw_protocol { } mixed_mode; }; -typedef struct efi_loaded_image { +union efi_simple_text_output_protocol { + struct { + void *reset; + efi_status_t (__efiapi *output_string)(efi_simple_text_output_protocol_t *, + efi_char16_t *); + void *test_string; + }; + struct { + u32 reset; + u32 output_string; + u32 test_string; + } mixed_mode; +}; + +#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 +#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1 +#define PIXEL_BIT_MASK 2 +#define PIXEL_BLT_ONLY 3 +#define PIXEL_FORMAT_MAX 4 + +typedef struct { + u32 red_mask; + u32 green_mask; + u32 blue_mask; + u32 reserved_mask; +} efi_pixel_bitmask_t; + +typedef struct { + u32 version; + u32 horizontal_resolution; + u32 vertical_resolution; + int pixel_format; + efi_pixel_bitmask_t pixel_information; + u32 pixels_per_scan_line; +} efi_graphics_output_mode_info_t; + +typedef union efi_graphics_output_protocol_mode efi_graphics_output_protocol_mode_t; + +union efi_graphics_output_protocol_mode { + struct { + u32 max_mode; + u32 mode; + efi_graphics_output_mode_info_t *info; + unsigned long size_of_info; + efi_physical_addr_t frame_buffer_base; + unsigned long frame_buffer_size; + }; + struct { + u32 max_mode; + u32 mode; + u32 info; + u32 size_of_info; + u64 frame_buffer_base; + u32 frame_buffer_size; + } mixed_mode; +}; + +typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t; + +union efi_graphics_output_protocol { + struct { + void *query_mode; + void *set_mode; + void *blt; + efi_graphics_output_protocol_mode_t *mode; + }; + struct { + u32 query_mode; + u32 set_mode; + u32 blt; + u32 mode; + } mixed_mode; +}; + +typedef struct { u32 revision; efi_handle_t parent_handle; efi_system_table_t *system_table; @@ -169,4 +365,257 @@ struct efi_simple_file_system_protocol { #define EFI_FILE_MODE_WRITE 0x0000000000000002 #define EFI_FILE_MODE_CREATE 0x8000000000000000 +typedef enum { + EfiPciIoWidthUint8, + EfiPciIoWidthUint16, + EfiPciIoWidthUint32, + EfiPciIoWidthUint64, + EfiPciIoWidthFifoUint8, + EfiPciIoWidthFifoUint16, + EfiPciIoWidthFifoUint32, + EfiPciIoWidthFifoUint64, + EfiPciIoWidthFillUint8, + EfiPciIoWidthFillUint16, + EfiPciIoWidthFillUint32, + EfiPciIoWidthFillUint64, + EfiPciIoWidthMaximum +} EFI_PCI_IO_PROTOCOL_WIDTH; + +typedef enum { + EfiPciIoAttributeOperationGet, + EfiPciIoAttributeOperationSet, + EfiPciIoAttributeOperationEnable, + EfiPciIoAttributeOperationDisable, + EfiPciIoAttributeOperationSupported, + EfiPciIoAttributeOperationMaximum +} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION; + +typedef struct { + u32 read; + u32 write; +} efi_pci_io_protocol_access_32_t; + +typedef union efi_pci_io_protocol efi_pci_io_protocol_t; + +typedef +efi_status_t (__efiapi *efi_pci_io_protocol_cfg_t)(efi_pci_io_protocol_t *, + EFI_PCI_IO_PROTOCOL_WIDTH, + u32 offset, + unsigned long count, + void *buffer); + +typedef struct { + void *read; + void *write; +} efi_pci_io_protocol_access_t; + +typedef struct { + efi_pci_io_protocol_cfg_t read; + efi_pci_io_protocol_cfg_t write; +} efi_pci_io_protocol_config_access_t; + +union efi_pci_io_protocol { + struct { + void *poll_mem; + void *poll_io; + efi_pci_io_protocol_access_t mem; + efi_pci_io_protocol_access_t io; + efi_pci_io_protocol_config_access_t pci; + void *copy_mem; + void *map; + void *unmap; + void *allocate_buffer; + void *free_buffer; + void *flush; + efi_status_t (__efiapi *get_location)(efi_pci_io_protocol_t *, + unsigned long *segment_nr, + unsigned long *bus_nr, + unsigned long *device_nr, + unsigned long *func_nr); + void *attributes; + void *get_bar_attributes; + void *set_bar_attributes; + uint64_t romsize; + void *romimage; + }; + struct { + u32 poll_mem; + u32 poll_io; + efi_pci_io_protocol_access_32_t mem; + efi_pci_io_protocol_access_32_t io; + efi_pci_io_protocol_access_32_t pci; + u32 copy_mem; + u32 map; + u32 unmap; + u32 allocate_buffer; + u32 free_buffer; + u32 flush; + u32 get_location; + u32 attributes; + u32 get_bar_attributes; + u32 set_bar_attributes; + u64 romsize; + u32 romimage; + } mixed_mode; +}; + +#define EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001 +#define EFI_PCI_IO_ATTRIBUTE_ISA_IO 0x0002 +#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO 0x0004 +#define EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY 0x0008 +#define EFI_PCI_IO_ATTRIBUTE_VGA_IO 0x0010 +#define EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO 0x0020 +#define EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO 0x0040 +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080 +#define EFI_PCI_IO_ATTRIBUTE_IO 0x0100 +#define EFI_PCI_IO_ATTRIBUTE_MEMORY 0x0200 +#define EFI_PCI_IO_ATTRIBUTE_BUS_MASTER 0x0400 +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED 0x0800 +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE 0x1000 +#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE 0x2000 +#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM 0x4000 +#define EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000 +#define EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 0x10000 +#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000 +#define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x40000 + +struct efi_dev_path; + +typedef union apple_properties_protocol apple_properties_protocol_t; + +union apple_properties_protocol { + struct { + unsigned long version; + efi_status_t (__efiapi *get)(apple_properties_protocol_t *, + struct efi_dev_path *, + efi_char16_t *, void *, u32 *); + efi_status_t (__efiapi *set)(apple_properties_protocol_t *, + struct efi_dev_path *, + efi_char16_t *, void *, u32); + efi_status_t (__efiapi *del)(apple_properties_protocol_t *, + struct efi_dev_path *, + efi_char16_t *); + efi_status_t (__efiapi *get_all)(apple_properties_protocol_t *, + void *buffer, u32 *); + }; + struct { + u32 version; + u32 get; + u32 set; + u32 del; + u32 get_all; + } mixed_mode; +}; + +typedef u32 efi_tcg2_event_log_format; + +typedef union efi_tcg2_protocol efi_tcg2_protocol_t; + +union efi_tcg2_protocol { + struct { + void *get_capability; + efi_status_t (__efiapi *get_event_log)(efi_handle_t, + efi_tcg2_event_log_format, + efi_physical_addr_t *, + efi_physical_addr_t *, + efi_bool_t *); + void *hash_log_extend_event; + void *submit_command; + void *get_active_pcr_banks; + void *set_active_pcr_banks; + void *get_result_of_set_active_pcr_banks; + }; + struct { + u32 get_capability; + u32 get_event_log; + u32 hash_log_extend_event; + u32 submit_command; + u32 get_active_pcr_banks; + u32 set_active_pcr_banks; + u32 get_result_of_set_active_pcr_banks; + } mixed_mode; +}; + +void efi_pci_disable_bridge_busmaster(void); + +typedef efi_status_t (*efi_exit_boot_map_processing)( + struct efi_boot_memmap *map, + void *priv); + +efi_status_t efi_exit_boot_services(void *handle, + struct efi_boot_memmap *map, + void *priv, + efi_exit_boot_map_processing priv_func); + +void efi_char16_printk(efi_char16_t *); + +unsigned long get_dram_base(void); + +efi_status_t allocate_new_fdt_and_exit_boot(void *handle, + unsigned long *new_fdt_addr, + unsigned long max_addr, + u64 initrd_addr, u64 initrd_size, + char *cmdline_ptr, + unsigned long fdt_addr, + unsigned long fdt_size); + +void *get_fdt(unsigned long *fdt_size); + +void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, + unsigned long desc_size, efi_memory_desc_t *runtime_map, + int *count); + +efi_status_t efi_get_random_bytes(unsigned long size, u8 *out); + +efi_status_t efi_random_alloc(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long random_seed); + +efi_status_t check_platform_features(void); + +void *get_efi_config_table(efi_guid_t guid); + +void efi_printk(char *str); + +void efi_free(unsigned long size, unsigned long addr); + +char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len); + +efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); + +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min); + +static inline +efi_status_t efi_low_alloc(unsigned long size, unsigned long align, + unsigned long *addr) +{ + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. Skip the first 8 + * bytes so we start at a nice even number. + */ + return efi_low_alloc_above(size, align, addr, 0x8); +} + +efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, + unsigned long max); + +efi_status_t efi_relocate_kernel(unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment, + unsigned long min_addr); + +efi_status_t handle_cmdline_files(efi_loaded_image_t *image, + char *cmd_line, char *option_string, + unsigned long max_addr, + unsigned long *load_addr, + unsigned long *load_size); + +efi_status_t efi_parse_options(char const *cmdline); + +efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, + unsigned long size); + #endif diff --git a/include/linux/efi.h b/include/linux/efi.h index 2b228df18407..0e047d2738cd 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -56,19 +56,6 @@ typedef void *efi_handle_t; #define __efiapi #endif -#define efi_get_handle_at(array, idx) \ - (efi_is_native() ? (array)[idx] \ - : (efi_handle_t)(unsigned long)((u32 *)(array))[idx]) - -#define efi_get_handle_num(size) \ - ((size) / (efi_is_native() ? sizeof(efi_handle_t) : sizeof(u32))) - -#define for_each_efi_handle(handle, array, size, i) \ - for (i = 0; \ - i < efi_get_handle_num(size) && \ - ((handle = efi_get_handle_at((array), i)) || true); \ - i++) - /* * The UEFI spec and EDK2 reference implementation both define EFI_GUID as * struct { u32 a; u16; b; u16 c; u8 d[8]; }; and so the implied alignment @@ -157,15 +144,6 @@ typedef struct { u32 imagesize; } efi_capsule_header_t; -struct efi_boot_memmap { - efi_memory_desc_t **map; - unsigned long *map_size; - unsigned long *desc_size; - u32 *desc_ver; - unsigned long *key_ptr; - unsigned long *buff_size; -}; - /* * EFI capsule flags */ @@ -187,14 +165,6 @@ struct capsule_info { int __efi_capsule_setup_info(struct capsule_info *cap_info); -/* - * Allocation types for calls to boottime->allocate_pages. - */ -#define EFI_ALLOCATE_ANY_PAGES 0 -#define EFI_ALLOCATE_MAX_ADDRESS 1 -#define EFI_ALLOCATE_ADDRESS 2 -#define EFI_MAX_ALLOCATE_TYPE 3 - typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg); /* @@ -224,291 +194,7 @@ typedef struct { u8 sets_to_zero; } efi_time_cap_t; -typedef struct { - efi_table_hdr_t hdr; - u32 raise_tpl; - u32 restore_tpl; - u32 allocate_pages; - u32 free_pages; - u32 get_memory_map; - u32 allocate_pool; - u32 free_pool; - u32 create_event; - u32 set_timer; - u32 wait_for_event; - u32 signal_event; - u32 close_event; - u32 check_event; - u32 install_protocol_interface; - u32 reinstall_protocol_interface; - u32 uninstall_protocol_interface; - u32 handle_protocol; - u32 __reserved; - u32 register_protocol_notify; - u32 locate_handle; - u32 locate_device_path; - u32 install_configuration_table; - u32 load_image; - u32 start_image; - u32 exit; - u32 unload_image; - u32 exit_boot_services; - u32 get_next_monotonic_count; - u32 stall; - u32 set_watchdog_timer; - u32 connect_controller; - u32 disconnect_controller; - u32 open_protocol; - u32 close_protocol; - u32 open_protocol_information; - u32 protocols_per_handle; - u32 locate_handle_buffer; - u32 locate_protocol; - u32 install_multiple_protocol_interfaces; - u32 uninstall_multiple_protocol_interfaces; - u32 calculate_crc32; - u32 copy_mem; - u32 set_mem; - u32 create_event_ex; -} __packed efi_boot_services_32_t; - -/* - * EFI Boot Services table - */ -typedef union { - struct { - efi_table_hdr_t hdr; - void *raise_tpl; - void *restore_tpl; - efi_status_t (__efiapi *allocate_pages)(int, int, unsigned long, - efi_physical_addr_t *); - efi_status_t (__efiapi *free_pages)(efi_physical_addr_t, - unsigned long); - efi_status_t (__efiapi *get_memory_map)(unsigned long *, void *, - unsigned long *, - unsigned long *, u32 *); - efi_status_t (__efiapi *allocate_pool)(int, unsigned long, - void **); - efi_status_t (__efiapi *free_pool)(void *); - void *create_event; - void *set_timer; - void *wait_for_event; - void *signal_event; - void *close_event; - void *check_event; - void *install_protocol_interface; - void *reinstall_protocol_interface; - void *uninstall_protocol_interface; - efi_status_t (__efiapi *handle_protocol)(efi_handle_t, - efi_guid_t *, void **); - void *__reserved; - void *register_protocol_notify; - efi_status_t (__efiapi *locate_handle)(int, efi_guid_t *, - void *, unsigned long *, - efi_handle_t *); - void *locate_device_path; - efi_status_t (__efiapi *install_configuration_table)(efi_guid_t *, - void *); - void *load_image; - void *start_image; - void *exit; - void *unload_image; - efi_status_t (__efiapi *exit_boot_services)(efi_handle_t, - unsigned long); - void *get_next_monotonic_count; - void *stall; - void *set_watchdog_timer; - void *connect_controller; - efi_status_t (__efiapi *disconnect_controller)(efi_handle_t, - efi_handle_t, - efi_handle_t); - void *open_protocol; - void *close_protocol; - void *open_protocol_information; - void *protocols_per_handle; - void *locate_handle_buffer; - efi_status_t (__efiapi *locate_protocol)(efi_guid_t *, void *, - void **); - void *install_multiple_protocol_interfaces; - void *uninstall_multiple_protocol_interfaces; - void *calculate_crc32; - void *copy_mem; - void *set_mem; - void *create_event_ex; - }; - efi_boot_services_32_t mixed_mode; -} efi_boot_services_t; - -typedef enum { - EfiPciIoWidthUint8, - EfiPciIoWidthUint16, - EfiPciIoWidthUint32, - EfiPciIoWidthUint64, - EfiPciIoWidthFifoUint8, - EfiPciIoWidthFifoUint16, - EfiPciIoWidthFifoUint32, - EfiPciIoWidthFifoUint64, - EfiPciIoWidthFillUint8, - EfiPciIoWidthFillUint16, - EfiPciIoWidthFillUint32, - EfiPciIoWidthFillUint64, - EfiPciIoWidthMaximum -} EFI_PCI_IO_PROTOCOL_WIDTH; - -typedef enum { - EfiPciIoAttributeOperationGet, - EfiPciIoAttributeOperationSet, - EfiPciIoAttributeOperationEnable, - EfiPciIoAttributeOperationDisable, - EfiPciIoAttributeOperationSupported, - EfiPciIoAttributeOperationMaximum -} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION; - -typedef struct { - u32 read; - u32 write; -} efi_pci_io_protocol_access_32_t; - -typedef union efi_pci_io_protocol efi_pci_io_protocol_t; - -typedef -efi_status_t (__efiapi *efi_pci_io_protocol_cfg_t)(efi_pci_io_protocol_t *, - EFI_PCI_IO_PROTOCOL_WIDTH, - u32 offset, - unsigned long count, - void *buffer); - -typedef struct { - void *read; - void *write; -} efi_pci_io_protocol_access_t; - -typedef struct { - efi_pci_io_protocol_cfg_t read; - efi_pci_io_protocol_cfg_t write; -} efi_pci_io_protocol_config_access_t; - -union efi_pci_io_protocol { - struct { - void *poll_mem; - void *poll_io; - efi_pci_io_protocol_access_t mem; - efi_pci_io_protocol_access_t io; - efi_pci_io_protocol_config_access_t pci; - void *copy_mem; - void *map; - void *unmap; - void *allocate_buffer; - void *free_buffer; - void *flush; - efi_status_t (__efiapi *get_location)(efi_pci_io_protocol_t *, - unsigned long *segment_nr, - unsigned long *bus_nr, - unsigned long *device_nr, - unsigned long *func_nr); - void *attributes; - void *get_bar_attributes; - void *set_bar_attributes; - uint64_t romsize; - void *romimage; - }; - struct { - u32 poll_mem; - u32 poll_io; - efi_pci_io_protocol_access_32_t mem; - efi_pci_io_protocol_access_32_t io; - efi_pci_io_protocol_access_32_t pci; - u32 copy_mem; - u32 map; - u32 unmap; - u32 allocate_buffer; - u32 free_buffer; - u32 flush; - u32 get_location; - u32 attributes; - u32 get_bar_attributes; - u32 set_bar_attributes; - u64 romsize; - u32 romimage; - } mixed_mode; -}; - -#define EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001 -#define EFI_PCI_IO_ATTRIBUTE_ISA_IO 0x0002 -#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO 0x0004 -#define EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY 0x0008 -#define EFI_PCI_IO_ATTRIBUTE_VGA_IO 0x0010 -#define EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO 0x0020 -#define EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO 0x0040 -#define EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080 -#define EFI_PCI_IO_ATTRIBUTE_IO 0x0100 -#define EFI_PCI_IO_ATTRIBUTE_MEMORY 0x0200 -#define EFI_PCI_IO_ATTRIBUTE_BUS_MASTER 0x0400 -#define EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED 0x0800 -#define EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE 0x1000 -#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE 0x2000 -#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM 0x4000 -#define EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000 -#define EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 0x10000 -#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000 -#define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x40000 - -struct efi_dev_path; - -typedef union apple_properties_protocol apple_properties_protocol_t; - -union apple_properties_protocol { - struct { - unsigned long version; - efi_status_t (__efiapi *get)(apple_properties_protocol_t *, - struct efi_dev_path *, - efi_char16_t *, void *, u32 *); - efi_status_t (__efiapi *set)(apple_properties_protocol_t *, - struct efi_dev_path *, - efi_char16_t *, void *, u32); - efi_status_t (__efiapi *del)(apple_properties_protocol_t *, - struct efi_dev_path *, - efi_char16_t *); - efi_status_t (__efiapi *get_all)(apple_properties_protocol_t *, - void *buffer, u32 *); - }; - struct { - u32 version; - u32 get; - u32 set; - u32 del; - u32 get_all; - } mixed_mode; -}; - -typedef u32 efi_tcg2_event_log_format; - -typedef union efi_tcg2_protocol efi_tcg2_protocol_t; - -union efi_tcg2_protocol { - struct { - void *get_capability; - efi_status_t (__efiapi *get_event_log)(efi_handle_t, - efi_tcg2_event_log_format, - efi_physical_addr_t *, - efi_physical_addr_t *, - efi_bool_t *); - void *hash_log_extend_event; - void *submit_command; - void *get_active_pcr_banks; - void *set_active_pcr_banks; - void *get_result_of_set_active_pcr_banks; - }; - struct { - u32 get_capability; - u32 get_event_log; - u32 hash_log_extend_event; - u32 submit_command; - u32 get_active_pcr_banks; - u32 set_active_pcr_banks; - u32 get_result_of_set_active_pcr_banks; - } mixed_mode; -}; +typedef union efi_boot_services efi_boot_services_t; /* * Types and defines for EFI ResetSystem @@ -796,8 +482,6 @@ struct efi_fdt_params { u32 desc_ver; }; -typedef struct efi_loaded_image efi_loaded_image_t; - typedef struct { u32 version; u32 length; @@ -1130,13 +814,6 @@ extern int efi_status_to_err(efi_status_t status); */ #define EFI_VARIABLE_GUID_LEN UUID_STRING_LEN -/* - * The type of search to perform when calling boottime->locate_handle - */ -#define EFI_LOCATE_ALL_HANDLES 0 -#define EFI_LOCATE_BY_REGISTER_NOTIFY 1 -#define EFI_LOCATE_BY_PROTOCOL 2 - /* * EFI Device Path information */ @@ -1254,80 +931,6 @@ struct efivar_entry { bool deleting; }; -union efi_simple_text_output_protocol { - struct { - void *reset; - efi_status_t (__efiapi *output_string)(efi_simple_text_output_protocol_t *, - efi_char16_t *); - void *test_string; - }; - struct { - u32 reset; - u32 output_string; - u32 test_string; - } mixed_mode; -}; - -#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 -#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1 -#define PIXEL_BIT_MASK 2 -#define PIXEL_BLT_ONLY 3 -#define PIXEL_FORMAT_MAX 4 - -typedef struct { - u32 red_mask; - u32 green_mask; - u32 blue_mask; - u32 reserved_mask; -} efi_pixel_bitmask_t; - -typedef struct { - u32 version; - u32 horizontal_resolution; - u32 vertical_resolution; - int pixel_format; - efi_pixel_bitmask_t pixel_information; - u32 pixels_per_scan_line; -} efi_graphics_output_mode_info_t; - -typedef union efi_graphics_output_protocol_mode efi_graphics_output_protocol_mode_t; - -union efi_graphics_output_protocol_mode { - struct { - u32 max_mode; - u32 mode; - efi_graphics_output_mode_info_t *info; - unsigned long size_of_info; - efi_physical_addr_t frame_buffer_base; - unsigned long frame_buffer_size; - }; - struct { - u32 max_mode; - u32 mode; - u32 info; - u32 size_of_info; - u64 frame_buffer_base; - u32 frame_buffer_size; - } mixed_mode; -}; - -typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t; - -union efi_graphics_output_protocol { - struct { - void *query_mode; - void *set_mode; - void *blt; - efi_graphics_output_protocol_mode_t *mode; - }; - struct { - u32 query_mode; - u32 set_mode; - u32 blt; - u32 mode; - } mixed_mode; -}; - extern struct list_head efivar_sysfs_list; static inline void @@ -1425,52 +1028,6 @@ static inline int efi_runtime_map_copy(void *buf, size_t bufsz) #endif -/* prototypes shared between arch specific and generic stub code */ - -void efi_printk(char *str); - -void efi_free(unsigned long size, unsigned long addr); - -char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len); - -efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); - -efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long min); - -static inline -efi_status_t efi_low_alloc(unsigned long size, unsigned long align, - unsigned long *addr) -{ - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. Skip the first 8 - * bytes so we start at a nice even number. - */ - return efi_low_alloc_above(size, align, addr, 0x8); -} - -efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, - unsigned long max); - -efi_status_t efi_relocate_kernel(unsigned long *image_addr, - unsigned long image_size, - unsigned long alloc_size, - unsigned long preferred_addr, - unsigned long alignment, - unsigned long min_addr); - -efi_status_t handle_cmdline_files(efi_loaded_image_t *image, - char *cmd_line, char *option_string, - unsigned long max_addr, - unsigned long *load_addr, - unsigned long *load_size); - -efi_status_t efi_parse_options(char const *cmdline); - -efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, - unsigned long size); - #ifdef CONFIG_EFI extern bool efi_runtime_disabled(void); #else @@ -1548,15 +1105,6 @@ void efi_retrieve_tpm2_eventlog(void); arch_efi_call_virt_teardown(); \ }) -typedef efi_status_t (*efi_exit_boot_map_processing)( - struct efi_boot_memmap *map, - void *priv); - -efi_status_t efi_exit_boot_services(void *handle, - struct efi_boot_memmap *map, - void *priv, - efi_exit_boot_map_processing priv_func); - #define EFI_RANDOM_SEED_SIZE 64U struct linux_efi_random_seed { @@ -1643,6 +1191,4 @@ struct linux_efi_memreserve { #define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \ / sizeof(((struct linux_efi_memreserve *)0)->entry[0])) -void efi_pci_disable_bridge_busmaster(void); - #endif /* _LINUX_EFI_H */ From 1e45bf7372c48c78e1f7e538fd3e612946f6ad20 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:40 +0100 Subject: [PATCH 0486/2231] efi/libstub/x86: Permit cmdline data to be allocated above 4 GB We now support cmdline data that is located in memory that is not 32-bit addressable, so relax the allocation limit on systems where this feature is enabled. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 2 -- drivers/firmware/efi/libstub/arm-stub.c | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 9 ++------- drivers/firmware/efi/libstub/efistub.h | 3 ++- drivers/firmware/efi/libstub/x86-stub.c | 5 +++-- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 86169a24b0d8..85f79accd3f8 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -34,8 +34,6 @@ static inline bool efi_have_uv1_memmap(void) #define EFI32_LOADER_SIGNATURE "EL32" #define EFI64_LOADER_SIGNATURE "EL64" -#define MAX_CMDLINE_ADDRESS UINT_MAX - #define ARCH_EFI_IRQ_FLAGS_MASK X86_EFLAGS_IF /* diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index e5c56306c641..39ce4c459ea7 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -169,7 +169,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) * protocol. We are going to copy the command line into the * device tree, so this can be allocated anywhere. */ - cmdline_ptr = efi_convert_cmdline(image, &cmdline_size); + cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX); if (!cmdline_ptr) { pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); status = EFI_OUT_OF_RESOURCES; diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 6db91655c743..92ccd0a51ae6 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -497,17 +497,13 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n) return dst; } -#ifndef MAX_CMDLINE_ADDRESS -#define MAX_CMDLINE_ADDRESS ULONG_MAX -#endif - /* * Convert the unicode UEFI command line to ASCII to pass to kernel. * Size of memory allocated return in *cmd_line_len. * Returns NULL on error. */ char *efi_convert_cmdline(efi_loaded_image_t *image, - int *cmd_line_len) + int *cmd_line_len, unsigned long max_addr) { const u16 *s2; u8 *s1 = NULL; @@ -535,8 +531,7 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, options_bytes++; /* NUL termination */ - status = efi_allocate_pages(options_bytes, &cmdline_addr, - MAX_CMDLINE_ADDRESS); + status = efi_allocate_pages(options_bytes, &cmdline_addr, max_addr); if (status != EFI_SUCCESS) return NULL; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 8bb46c1fd2cd..b94c63d17a4f 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -578,7 +578,8 @@ void efi_printk(char *str); void efi_free(unsigned long size, unsigned long addr); -char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len); +char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len, + unsigned long max_addr); efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 9d60352baa0f..f2dbf119837c 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -405,7 +405,8 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, hdr->type_of_loader = 0x21; /* Convert unicode cmdline to ascii */ - cmdline_ptr = efi_convert_cmdline(image, &options_size); + cmdline_ptr = efi_convert_cmdline(image, &options_size, + above4g ? ULONG_MAX : UINT_MAX); if (!cmdline_ptr) goto fail; @@ -445,7 +446,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, /* not reached */ fail2: - efi_free(options_size, hdr->cmd_line_ptr); + efi_free(options_size, (unsigned long)cmdline_ptr); fail: efi_free(0x4000, (unsigned long)boot_params); From 0ed02bdaa7d8d93157a7055a23b03774c930f561 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:41 +0100 Subject: [PATCH 0487/2231] efi/libstub: Move efi_random_alloc() into separate source file efi_random_alloc() is only used on arm64, but as it shares a source file with efi_random_get_seed(), the latter will pull in the former on other architectures as well. Let's take advantage of the fact that libstub is a static library, and so the linker will only incorporate objects that are needed to satisfy dependencies in other objects. This means we can move the random alloc code to a separate source file that gets built unconditionally, but only used when needed. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/random.c | 114 ------------------- drivers/firmware/efi/libstub/randomalloc.c | 124 +++++++++++++++++++++ 3 files changed, 125 insertions(+), 115 deletions(-) create mode 100644 drivers/firmware/efi/libstub/randomalloc.c diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index bda59d82b7f6..a07b044131d1 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -40,7 +40,7 @@ OBJECT_FILES_NON_STANDARD := y KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ - mem.o random.o pci.o + mem.o random.o randomalloc.o pci.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index 316ce9ff0193..21e7e9325219 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -4,7 +4,6 @@ */ #include -#include #include #include "efistub.h" @@ -39,119 +38,6 @@ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out) return efi_call_proto(rng, get_rng, NULL, size, out); } -/* - * Return the number of slots covered by this entry, i.e., the number of - * addresses it covers that are suitably aligned and supply enough room - * for the allocation. - */ -static unsigned long get_entry_num_slots(efi_memory_desc_t *md, - unsigned long size, - unsigned long align_shift) -{ - unsigned long align = 1UL << align_shift; - u64 first_slot, last_slot, region_end; - - if (md->type != EFI_CONVENTIONAL_MEMORY) - return 0; - - if (efi_soft_reserve_enabled() && - (md->attribute & EFI_MEMORY_SP)) - return 0; - - region_end = min((u64)ULONG_MAX, md->phys_addr + md->num_pages*EFI_PAGE_SIZE - 1); - - first_slot = round_up(md->phys_addr, align); - last_slot = round_down(region_end - size + 1, align); - - if (first_slot > last_slot) - return 0; - - return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1; -} - -/* - * The UEFI memory descriptors have a virtual address field that is only used - * when installing the virtual mapping using SetVirtualAddressMap(). Since it - * is unused here, we can reuse it to keep track of each descriptor's slot - * count. - */ -#define MD_NUM_SLOTS(md) ((md)->virt_addr) - -efi_status_t efi_random_alloc(unsigned long size, - unsigned long align, - unsigned long *addr, - unsigned long random_seed) -{ - unsigned long map_size, desc_size, total_slots = 0, target_slot; - unsigned long buff_size; - efi_status_t status; - efi_memory_desc_t *memory_map; - int map_offset; - struct efi_boot_memmap map; - - map.map = &memory_map; - map.map_size = &map_size; - map.desc_size = &desc_size; - map.desc_ver = NULL; - map.key_ptr = NULL; - map.buff_size = &buff_size; - - status = efi_get_memory_map(&map); - if (status != EFI_SUCCESS) - return status; - - if (align < EFI_ALLOC_ALIGN) - align = EFI_ALLOC_ALIGN; - - /* count the suitable slots in each memory map entry */ - for (map_offset = 0; map_offset < map_size; map_offset += desc_size) { - efi_memory_desc_t *md = (void *)memory_map + map_offset; - unsigned long slots; - - slots = get_entry_num_slots(md, size, ilog2(align)); - MD_NUM_SLOTS(md) = slots; - total_slots += slots; - } - - /* find a random number between 0 and total_slots */ - target_slot = (total_slots * (u16)random_seed) >> 16; - - /* - * target_slot is now a value in the range [0, total_slots), and so - * it corresponds with exactly one of the suitable slots we recorded - * when iterating over the memory map the first time around. - * - * So iterate over the memory map again, subtracting the number of - * slots of each entry at each iteration, until we have found the entry - * that covers our chosen slot. Use the residual value of target_slot - * to calculate the randomly chosen address, and allocate it directly - * using EFI_ALLOCATE_ADDRESS. - */ - for (map_offset = 0; map_offset < map_size; map_offset += desc_size) { - efi_memory_desc_t *md = (void *)memory_map + map_offset; - efi_physical_addr_t target; - unsigned long pages; - - if (target_slot >= MD_NUM_SLOTS(md)) { - target_slot -= MD_NUM_SLOTS(md); - continue; - } - - target = round_up(md->phys_addr, align) + target_slot * align; - pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, pages, &target); - if (status == EFI_SUCCESS) - *addr = target; - break; - } - - efi_bs_call(free_pool, memory_map); - - return status; -} - efi_status_t efi_random_get_seed(void) { efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c new file mode 100644 index 000000000000..4578f59e160c --- /dev/null +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Linaro Ltd; + */ + +#include +#include +#include + +#include "efistub.h" + +/* + * Return the number of slots covered by this entry, i.e., the number of + * addresses it covers that are suitably aligned and supply enough room + * for the allocation. + */ +static unsigned long get_entry_num_slots(efi_memory_desc_t *md, + unsigned long size, + unsigned long align_shift) +{ + unsigned long align = 1UL << align_shift; + u64 first_slot, last_slot, region_end; + + if (md->type != EFI_CONVENTIONAL_MEMORY) + return 0; + + if (efi_soft_reserve_enabled() && + (md->attribute & EFI_MEMORY_SP)) + return 0; + + region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1, + (u64)ULONG_MAX); + + first_slot = round_up(md->phys_addr, align); + last_slot = round_down(region_end - size + 1, align); + + if (first_slot > last_slot) + return 0; + + return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1; +} + +/* + * The UEFI memory descriptors have a virtual address field that is only used + * when installing the virtual mapping using SetVirtualAddressMap(). Since it + * is unused here, we can reuse it to keep track of each descriptor's slot + * count. + */ +#define MD_NUM_SLOTS(md) ((md)->virt_addr) + +efi_status_t efi_random_alloc(unsigned long size, + unsigned long align, + unsigned long *addr, + unsigned long random_seed) +{ + unsigned long map_size, desc_size, total_slots = 0, target_slot; + unsigned long buff_size; + efi_status_t status; + efi_memory_desc_t *memory_map; + int map_offset; + struct efi_boot_memmap map; + + map.map = &memory_map; + map.map_size = &map_size; + map.desc_size = &desc_size; + map.desc_ver = NULL; + map.key_ptr = NULL; + map.buff_size = &buff_size; + + status = efi_get_memory_map(&map); + if (status != EFI_SUCCESS) + return status; + + if (align < EFI_ALLOC_ALIGN) + align = EFI_ALLOC_ALIGN; + + /* count the suitable slots in each memory map entry */ + for (map_offset = 0; map_offset < map_size; map_offset += desc_size) { + efi_memory_desc_t *md = (void *)memory_map + map_offset; + unsigned long slots; + + slots = get_entry_num_slots(md, size, ilog2(align)); + MD_NUM_SLOTS(md) = slots; + total_slots += slots; + } + + /* find a random number between 0 and total_slots */ + target_slot = (total_slots * (u16)random_seed) >> 16; + + /* + * target_slot is now a value in the range [0, total_slots), and so + * it corresponds with exactly one of the suitable slots we recorded + * when iterating over the memory map the first time around. + * + * So iterate over the memory map again, subtracting the number of + * slots of each entry at each iteration, until we have found the entry + * that covers our chosen slot. Use the residual value of target_slot + * to calculate the randomly chosen address, and allocate it directly + * using EFI_ALLOCATE_ADDRESS. + */ + for (map_offset = 0; map_offset < map_size; map_offset += desc_size) { + efi_memory_desc_t *md = (void *)memory_map + map_offset; + efi_physical_addr_t target; + unsigned long pages; + + if (target_slot >= MD_NUM_SLOTS(md)) { + target_slot -= MD_NUM_SLOTS(md); + continue; + } + + target = round_up(md->phys_addr, align) + target_slot * align; + pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, pages, &target); + if (status == EFI_SUCCESS) + *addr = target; + break; + } + + efi_bs_call(free_pool, memory_map); + + return status; +} From b87174541abcad8d37ab799fa431ca225175ca19 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:42 +0100 Subject: [PATCH 0488/2231] efi/libstub: Move get_dram_base() into arm-stub.c get_dram_base() is only called from arm-stub.c so move it into the same source file as its caller. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm-stub.c | 33 +++++++++++++++++ .../firmware/efi/libstub/efi-stub-helper.c | 35 ------------------- drivers/firmware/efi/libstub/efistub.h | 2 -- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 39ce4c459ea7..1e0ff23ef3c3 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -88,6 +88,39 @@ void install_memreserve_table(void) pr_efi_err("Failed to install memreserve config table!\n"); } +static unsigned long get_dram_base(void) +{ + efi_status_t status; + unsigned long map_size, buff_size; + unsigned long membase = EFI_ERROR; + struct efi_memory_map map; + efi_memory_desc_t *md; + struct efi_boot_memmap boot_map; + + boot_map.map = (efi_memory_desc_t **)&map.map; + boot_map.map_size = &map_size; + boot_map.desc_size = &map.desc_size; + boot_map.desc_ver = NULL; + boot_map.key_ptr = NULL; + boot_map.buff_size = &buff_size; + + status = efi_get_memory_map(&boot_map); + if (status != EFI_SUCCESS) + return membase; + + map.map_end = map.map + map_size; + + for_each_efi_memory_desc_in_map(&map, md) { + if (md->attribute & EFI_MEMORY_WB) { + if (membase > md->phys_addr) + membase = md->phys_addr; + } + } + + efi_bs_call(free_pool, map.map); + + return membase; +} /* * This function handles the architcture specific differences between arm and diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 92ccd0a51ae6..1a8f2cf5a2bd 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -75,41 +75,6 @@ void efi_printk(char *str) } } - -unsigned long get_dram_base(void) -{ - efi_status_t status; - unsigned long map_size, buff_size; - unsigned long membase = EFI_ERROR; - struct efi_memory_map map; - efi_memory_desc_t *md; - struct efi_boot_memmap boot_map; - - boot_map.map = (efi_memory_desc_t **)&map.map; - boot_map.map_size = &map_size; - boot_map.desc_size = &map.desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - return membase; - - map.map_end = map.map + map_size; - - for_each_efi_memory_desc_in_map(&map, md) { - if (md->attribute & EFI_MEMORY_WB) { - if (membase > md->phys_addr) - membase = md->phys_addr; - } - } - - efi_bs_call(free_pool, map.map); - - return membase; -} - static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16, void **handle, u64 *file_sz) { diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index b94c63d17a4f..5123def761e9 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -549,8 +549,6 @@ efi_status_t efi_exit_boot_services(void *handle, void efi_char16_printk(efi_char16_t *); -unsigned long get_dram_base(void); - efi_status_t allocate_new_fdt_and_exit_boot(void *handle, unsigned long *new_fdt_addr, unsigned long max_addr, From 5193a33d78ce8b4fdf880a754700bce21b3756b5 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:43 +0100 Subject: [PATCH 0489/2231] efi/libstub: Move file I/O support code into separate file Split off the file I/O support code into a separate source file so it ends up in a separate object file in the static library, allowing the linker to omit it if the routines are not used. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 2 +- .../firmware/efi/libstub/efi-stub-helper.c | 294 +---------------- drivers/firmware/efi/libstub/efistub.h | 1 + drivers/firmware/efi/libstub/file.c | 303 ++++++++++++++++++ 4 files changed, 311 insertions(+), 289 deletions(-) create mode 100644 drivers/firmware/efi/libstub/file.c diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index a07b044131d1..1202c9ee0ea9 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -40,7 +40,7 @@ OBJECT_FILES_NON_STANDARD := y KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ - mem.o random.o randomalloc.o pci.o + file.o mem.o random.o randomalloc.o pci.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 1a8f2cf5a2bd..db23be5dc69b 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -12,23 +12,7 @@ #include "efistub.h" -/* - * Some firmware implementations have problems reading files in one go. - * A read chunk size of 1MB seems to work for most platforms. - * - * Unfortunately, reading files in chunks triggers *other* bugs on some - * platforms, so we provide a way to disable this workaround, which can - * be done by passing "efi=nochunk" on the EFI boot stub command line. - * - * If you experience issues with initrd images being corrupt it's worth - * trying efi=nochunk, but chunking is enabled by default because there - * are far more machines that require the workaround than those that - * break with it enabled. - */ -#define EFI_READ_CHUNK_SIZE (1024 * 1024) - -static unsigned long efi_chunk_size = EFI_READ_CHUNK_SIZE; - +static bool __efistub_global efi_nochunk; static bool __efistub_global efi_nokaslr; static bool __efistub_global efi_quiet; static bool __efistub_global efi_novamap; @@ -36,6 +20,10 @@ static bool __efistub_global efi_nosoftreserve; static bool __efistub_global efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); +bool __pure nochunk(void) +{ + return efi_nochunk; +} bool __pure nokaslr(void) { return efi_nokaslr; @@ -53,11 +41,6 @@ bool __pure __efi_soft_reserve_enabled(void) return !efi_nosoftreserve; } -struct file_info { - efi_file_protocol_t *handle; - u64 size; -}; - void efi_printk(char *str) { char *s8; @@ -75,90 +58,6 @@ void efi_printk(char *str) } } -static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16, - void **handle, u64 *file_sz) -{ - efi_file_protocol_t *h, *fh = __fh; - efi_file_info_t *info; - efi_status_t status; - efi_guid_t info_guid = EFI_FILE_INFO_ID; - unsigned long info_sz; - - status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, 0); - if (status != EFI_SUCCESS) { - efi_printk("Failed to open file: "); - efi_char16_printk(filename_16); - efi_printk("\n"); - return status; - } - - *handle = h; - - info_sz = 0; - status = h->get_info(h, &info_guid, &info_sz, NULL); - if (status != EFI_BUFFER_TOO_SMALL) { - efi_printk("Failed to get file info size\n"); - return status; - } - -grow: - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, info_sz, - (void **)&info); - if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for file info\n"); - return status; - } - - status = h->get_info(h, &info_guid, &info_sz, info); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_bs_call(free_pool, info); - goto grow; - } - - *file_sz = info->file_size; - efi_bs_call(free_pool, info); - - if (status != EFI_SUCCESS) - efi_printk("Failed to get initrd info\n"); - - return status; -} - -static efi_status_t efi_file_read(efi_file_protocol_t *handle, - unsigned long *size, void *addr) -{ - return handle->read(handle, size, addr); -} - -static efi_status_t efi_file_close(efi_file_protocol_t *handle) -{ - return handle->close(handle); -} - -static efi_status_t efi_open_volume(efi_loaded_image_t *image, - efi_file_protocol_t **__fh) -{ - efi_simple_file_system_protocol_t *io; - efi_file_protocol_t *fh; - efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; - efi_status_t status; - efi_handle_t handle = image->device_handle; - - status = efi_bs_call(handle_protocol, handle, &fs_proto, (void **)&io); - if (status != EFI_SUCCESS) { - efi_printk("Failed to handle fs_proto\n"); - return status; - } - - status = io->open_volume(io, &fh); - if (status != EFI_SUCCESS) - efi_printk("Failed to open volume\n"); - else - *__fh = fh; - - return status; -} - /* * Parse the ASCII string 'cmdline' for EFI options, denoted by the efi= * option, e.g. efi=nochunk. @@ -197,7 +96,7 @@ efi_status_t efi_parse_options(char const *cmdline) while (*str && *str != ' ') { if (!strncmp(str, "nochunk", 7)) { str += strlen("nochunk"); - efi_chunk_size = -1UL; + efi_nochunk = true; } if (!strncmp(str, "novamap", 7)) { @@ -232,187 +131,6 @@ efi_status_t efi_parse_options(char const *cmdline) return EFI_SUCCESS; } -/* - * Check the cmdline for a LILO-style file= arguments. - * - * We only support loading a file from the same filesystem as - * the kernel image. - */ -efi_status_t handle_cmdline_files(efi_loaded_image_t *image, - char *cmd_line, char *option_string, - unsigned long max_addr, - unsigned long *load_addr, - unsigned long *load_size) -{ - struct file_info *files; - unsigned long file_addr; - u64 file_size_total; - efi_file_protocol_t *fh = NULL; - efi_status_t status; - int nr_files; - char *str; - int i, j, k; - - file_addr = 0; - file_size_total = 0; - - str = cmd_line; - - j = 0; /* See close_handles */ - - if (!load_addr || !load_size) - return EFI_INVALID_PARAMETER; - - *load_addr = 0; - *load_size = 0; - - if (!str || !*str) - return EFI_SUCCESS; - - for (nr_files = 0; *str; nr_files++) { - str = strstr(str, option_string); - if (!str) - break; - - str += strlen(option_string); - - /* Skip any leading slashes */ - while (*str == '/' || *str == '\\') - str++; - - while (*str && *str != ' ' && *str != '\n') - str++; - } - - if (!nr_files) - return EFI_SUCCESS; - - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, - nr_files * sizeof(*files), (void **)&files); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to alloc mem for file handle list\n"); - goto fail; - } - - str = cmd_line; - for (i = 0; i < nr_files; i++) { - struct file_info *file; - efi_char16_t filename_16[256]; - efi_char16_t *p; - - str = strstr(str, option_string); - if (!str) - break; - - str += strlen(option_string); - - file = &files[i]; - p = filename_16; - - /* Skip any leading slashes */ - while (*str == '/' || *str == '\\') - str++; - - while (*str && *str != ' ' && *str != '\n') { - if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) - break; - - if (*str == '/') { - *p++ = '\\'; - str++; - } else { - *p++ = *str++; - } - } - - *p = '\0'; - - /* Only open the volume once. */ - if (!i) { - status = efi_open_volume(image, &fh); - if (status != EFI_SUCCESS) - goto free_files; - } - - status = efi_file_size(fh, filename_16, (void **)&file->handle, - &file->size); - if (status != EFI_SUCCESS) - goto close_handles; - - file_size_total += file->size; - } - - if (file_size_total) { - unsigned long addr; - - /* - * Multiple files need to be at consecutive addresses in memory, - * so allocate enough memory for all the files. This is used - * for loading multiple files. - */ - status = efi_allocate_pages(file_size_total, &file_addr, max_addr); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to alloc highmem for files\n"); - goto close_handles; - } - - /* We've run out of free low memory. */ - if (file_addr > max_addr) { - pr_efi_err("We've run out of free low memory\n"); - status = EFI_INVALID_PARAMETER; - goto free_file_total; - } - - addr = file_addr; - for (j = 0; j < nr_files; j++) { - unsigned long size; - - size = files[j].size; - while (size) { - unsigned long chunksize; - - if (IS_ENABLED(CONFIG_X86) && size > efi_chunk_size) - chunksize = efi_chunk_size; - else - chunksize = size; - - status = efi_file_read(files[j].handle, - &chunksize, - (void *)addr); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to read file\n"); - goto free_file_total; - } - addr += chunksize; - size -= chunksize; - } - - efi_file_close(files[j].handle); - } - - } - - efi_bs_call(free_pool, files); - - *load_addr = file_addr; - *load_size = file_size_total; - - return status; - -free_file_total: - efi_free(file_size_total, file_addr); - -close_handles: - for (k = j; k < i; k++) - efi_file_close(files[k].handle); -free_files: - efi_bs_call(free_pool, files); -fail: - *load_addr = 0; - *load_size = 0; - - return status; -} /* * Get the number of UTF-8 bytes corresponding to an UTF-16 character. * This overestimates for surrogates, but that is okay. diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 5123def761e9..e057d509d5d8 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -31,6 +31,7 @@ #define __efistub_global #endif +extern bool __pure nochunk(void); extern bool __pure nokaslr(void); extern bool __pure is_quiet(void); extern bool __pure novamap(void); diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c new file mode 100644 index 000000000000..e0302f340962 --- /dev/null +++ b/drivers/firmware/efi/libstub/file.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Helper functions used by the EFI stub on multiple + * architectures. This should be #included by the EFI stub + * implementation files. + * + * Copyright 2011 Intel Corporation; author Matt Fleming + */ + +#include +#include + +#include "efistub.h" + +/* + * Some firmware implementations have problems reading files in one go. + * A read chunk size of 1MB seems to work for most platforms. + * + * Unfortunately, reading files in chunks triggers *other* bugs on some + * platforms, so we provide a way to disable this workaround, which can + * be done by passing "efi=nochunk" on the EFI boot stub command line. + * + * If you experience issues with initrd images being corrupt it's worth + * trying efi=nochunk, but chunking is enabled by default on x86 because + * there are far more machines that require the workaround than those that + * break with it enabled. + */ +#define EFI_READ_CHUNK_SIZE SZ_1M + +struct file_info { + efi_file_protocol_t *handle; + u64 size; +}; + +static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16, + void **handle, u64 *file_sz) +{ + efi_file_protocol_t *h, *fh = __fh; + efi_file_info_t *info; + efi_status_t status; + efi_guid_t info_guid = EFI_FILE_INFO_ID; + unsigned long info_sz; + + status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, 0); + if (status != EFI_SUCCESS) { + efi_printk("Failed to open file: "); + efi_char16_printk(filename_16); + efi_printk("\n"); + return status; + } + + *handle = h; + + info_sz = 0; + status = h->get_info(h, &info_guid, &info_sz, NULL); + if (status != EFI_BUFFER_TOO_SMALL) { + efi_printk("Failed to get file info size\n"); + return status; + } + +grow: + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, info_sz, + (void **)&info); + if (status != EFI_SUCCESS) { + efi_printk("Failed to alloc mem for file info\n"); + return status; + } + + status = h->get_info(h, &info_guid, &info_sz, info); + if (status == EFI_BUFFER_TOO_SMALL) { + efi_bs_call(free_pool, info); + goto grow; + } + + *file_sz = info->file_size; + efi_bs_call(free_pool, info); + + if (status != EFI_SUCCESS) + efi_printk("Failed to get initrd info\n"); + + return status; +} + +static efi_status_t efi_file_read(efi_file_protocol_t *handle, + unsigned long *size, void *addr) +{ + return handle->read(handle, size, addr); +} + +static efi_status_t efi_file_close(efi_file_protocol_t *handle) +{ + return handle->close(handle); +} + +static efi_status_t efi_open_volume(efi_loaded_image_t *image, + efi_file_protocol_t **__fh) +{ + efi_simple_file_system_protocol_t *io; + efi_file_protocol_t *fh; + efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; + efi_status_t status; + efi_handle_t handle = image->device_handle; + + status = efi_bs_call(handle_protocol, handle, &fs_proto, (void **)&io); + if (status != EFI_SUCCESS) { + efi_printk("Failed to handle fs_proto\n"); + return status; + } + + status = io->open_volume(io, &fh); + if (status != EFI_SUCCESS) + efi_printk("Failed to open volume\n"); + else + *__fh = fh; + + return status; +} + +/* + * Check the cmdline for a LILO-style file= arguments. + * + * We only support loading a file from the same filesystem as + * the kernel image. + */ +efi_status_t handle_cmdline_files(efi_loaded_image_t *image, + char *cmd_line, char *option_string, + unsigned long max_addr, + unsigned long *load_addr, + unsigned long *load_size) +{ + unsigned long efi_chunk_size = ULONG_MAX; + struct file_info *files; + unsigned long file_addr; + u64 file_size_total; + efi_file_protocol_t *fh = NULL; + efi_status_t status; + int nr_files; + char *str; + int i, j, k; + + if (IS_ENABLED(CONFIG_X86) && !nochunk()) + efi_chunk_size = EFI_READ_CHUNK_SIZE; + + file_addr = 0; + file_size_total = 0; + + str = cmd_line; + + j = 0; /* See close_handles */ + + if (!load_addr || !load_size) + return EFI_INVALID_PARAMETER; + + *load_addr = 0; + *load_size = 0; + + if (!str || !*str) + return EFI_SUCCESS; + + for (nr_files = 0; *str; nr_files++) { + str = strstr(str, option_string); + if (!str) + break; + + str += strlen(option_string); + + /* Skip any leading slashes */ + while (*str == '/' || *str == '\\') + str++; + + while (*str && *str != ' ' && *str != '\n') + str++; + } + + if (!nr_files) + return EFI_SUCCESS; + + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, + nr_files * sizeof(*files), (void **)&files); + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to alloc mem for file handle list\n"); + goto fail; + } + + str = cmd_line; + for (i = 0; i < nr_files; i++) { + struct file_info *file; + efi_char16_t filename_16[256]; + efi_char16_t *p; + + str = strstr(str, option_string); + if (!str) + break; + + str += strlen(option_string); + + file = &files[i]; + p = filename_16; + + /* Skip any leading slashes */ + while (*str == '/' || *str == '\\') + str++; + + while (*str && *str != ' ' && *str != '\n') { + if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) + break; + + if (*str == '/') { + *p++ = '\\'; + str++; + } else { + *p++ = *str++; + } + } + + *p = '\0'; + + /* Only open the volume once. */ + if (!i) { + status = efi_open_volume(image, &fh); + if (status != EFI_SUCCESS) + goto free_files; + } + + status = efi_file_size(fh, filename_16, (void **)&file->handle, + &file->size); + if (status != EFI_SUCCESS) + goto close_handles; + + file_size_total += file->size; + } + + if (file_size_total) { + unsigned long addr; + + /* + * Multiple files need to be at consecutive addresses in memory, + * so allocate enough memory for all the files. This is used + * for loading multiple files. + */ + status = efi_allocate_pages(file_size_total, &file_addr, max_addr); + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to alloc highmem for files\n"); + goto close_handles; + } + + /* We've run out of free low memory. */ + if (file_addr > max_addr) { + pr_efi_err("We've run out of free low memory\n"); + status = EFI_INVALID_PARAMETER; + goto free_file_total; + } + + addr = file_addr; + for (j = 0; j < nr_files; j++) { + unsigned long size; + + size = files[j].size; + while (size) { + unsigned long chunksize; + + if (size > efi_chunk_size) + chunksize = efi_chunk_size; + else + chunksize = size; + + status = efi_file_read(files[j].handle, + &chunksize, + (void *)addr); + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to read file\n"); + goto free_file_total; + } + addr += chunksize; + size -= chunksize; + } + + efi_file_close(files[j].handle); + } + + } + + efi_bs_call(free_pool, files); + + *load_addr = file_addr; + *load_size = file_size_total; + + return status; + +free_file_total: + efi_free(file_size_total, file_addr); + +close_handles: + for (k = j; k < i; k++) + efi_file_close(files[k].handle); +free_files: + efi_bs_call(free_pool, files); +fail: + *load_addr = 0; + *load_size = 0; + + return status; +} From 9302c1bb8e475829330146423626c3d32e8cb012 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:44 +0100 Subject: [PATCH 0490/2231] efi/libstub: Rewrite file I/O routine The file I/O routine that is used to load initrd or dtb files from the EFI system partition suffers from a few issues: - it converts the u8[] command line back to a UTF-16 string, which is pointless since we only handle initrd or dtb arguments provided via the loaded image protocol anyway, which is where we got the UTF-16[] command line from in the first place when booting via the PE entry point, - in the far majority of cases, only a single initrd= option is present, but it optimizes for multiple options, by going over the command line twice, allocating heap buffers for dynamically sized arrays, etc. - the coding style is hard to follow, with few comments, and all logic including string parsing etc all combined in a single routine. Let's fix this by rewriting most of it, based on the idea that in the case of multiple initrds, we can just allocate a new, bigger buffer and copy over the data before freeing the old one. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm-stub.c | 12 +- drivers/firmware/efi/libstub/efistub.h | 17 +- drivers/firmware/efi/libstub/file.c | 358 ++++++++++-------------- drivers/firmware/efi/libstub/x86-stub.c | 12 +- 4 files changed, 170 insertions(+), 229 deletions(-) diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 1e0ff23ef3c3..ad44783ae128 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -154,7 +154,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) unsigned long dram_base; /* addr/point and size pairs for memory management*/ unsigned long initrd_addr; - u64 initrd_size = 0; + unsigned long initrd_size = 0; unsigned long fdt_addr = 0; /* Original DTB */ unsigned long fdt_size = 0; char *cmdline_ptr = NULL; @@ -247,8 +247,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (strstr(cmdline_ptr, "dtb=")) pr_efi("Ignoring DTB from command line.\n"); } else { - status = handle_cmdline_files(image, cmdline_ptr, "dtb=", - ~0UL, &fdt_addr, &fdt_size); + status = efi_load_dtb(image, &fdt_addr, &fdt_size); if (status != EFI_SUCCESS) { pr_efi_err("Failed to load device tree!\n"); @@ -268,11 +267,8 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!fdt_addr) pr_efi("Generating empty DTB\n"); - status = handle_cmdline_files(image, cmdline_ptr, "initrd=", - efi_get_max_initrd_addr(dram_base, - image_addr), - (unsigned long *)&initrd_addr, - (unsigned long *)&initrd_size); + status = efi_load_initrd(image, &initrd_addr, &initrd_size, + efi_get_max_initrd_addr(dram_base, image_addr)); if (status != EFI_SUCCESS) pr_efi_err("Failed initrd from command line!\n"); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index e057d509d5d8..60d929469b8b 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -327,7 +327,7 @@ typedef struct { efi_time_t last_access_time; efi_time_t modification_time; __aligned_u64 attribute; - efi_char16_t filename[1]; + efi_char16_t filename[]; } efi_file_info_t; typedef struct efi_file_protocol efi_file_protocol_t; @@ -607,15 +607,18 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr, unsigned long alignment, unsigned long min_addr); -efi_status_t handle_cmdline_files(efi_loaded_image_t *image, - char *cmd_line, char *option_string, - unsigned long max_addr, - unsigned long *load_addr, - unsigned long *load_size); - efi_status_t efi_parse_options(char const *cmdline); efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, unsigned long size); +efi_status_t efi_load_dtb(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size); + +efi_status_t efi_load_initrd(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size, + unsigned long max_addr); + #endif diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index e0302f340962..0d67432ed067 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -12,6 +12,8 @@ #include "efistub.h" +#define MAX_FILENAME_SIZE 256 + /* * Some firmware implementations have problems reading files in one go. * A read chunk size of 1MB seems to work for most platforms. @@ -27,277 +29,221 @@ */ #define EFI_READ_CHUNK_SIZE SZ_1M -struct file_info { - efi_file_protocol_t *handle; - u64 size; -}; - -static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16, - void **handle, u64 *file_sz) +static efi_status_t efi_open_file(efi_file_protocol_t *volume, + efi_char16_t *filename_16, + efi_file_protocol_t **handle, + unsigned long *file_size) { - efi_file_protocol_t *h, *fh = __fh; - efi_file_info_t *info; - efi_status_t status; + struct { + efi_file_info_t info; + efi_char16_t filename[MAX_FILENAME_SIZE]; + } finfo; efi_guid_t info_guid = EFI_FILE_INFO_ID; + efi_file_protocol_t *fh; unsigned long info_sz; + efi_status_t status; - status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, 0); + status = volume->open(volume, &fh, filename_16, EFI_FILE_MODE_READ, 0); if (status != EFI_SUCCESS) { - efi_printk("Failed to open file: "); + pr_efi_err("Failed to open file: "); efi_char16_printk(filename_16); efi_printk("\n"); return status; } - *handle = h; - - info_sz = 0; - status = h->get_info(h, &info_guid, &info_sz, NULL); - if (status != EFI_BUFFER_TOO_SMALL) { - efi_printk("Failed to get file info size\n"); - return status; - } - -grow: - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, info_sz, - (void **)&info); + info_sz = sizeof(finfo); + status = fh->get_info(fh, &info_guid, &info_sz, &finfo); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for file info\n"); + pr_efi_err("Failed to get file info\n"); + fh->close(fh); return status; } - status = h->get_info(h, &info_guid, &info_sz, info); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_bs_call(free_pool, info); - goto grow; - } - - *file_sz = info->file_size; - efi_bs_call(free_pool, info); - - if (status != EFI_SUCCESS) - efi_printk("Failed to get initrd info\n"); - - return status; -} - -static efi_status_t efi_file_read(efi_file_protocol_t *handle, - unsigned long *size, void *addr) -{ - return handle->read(handle, size, addr); -} - -static efi_status_t efi_file_close(efi_file_protocol_t *handle) -{ - return handle->close(handle); + *handle = fh; + *file_size = finfo.info.file_size; + return EFI_SUCCESS; } static efi_status_t efi_open_volume(efi_loaded_image_t *image, - efi_file_protocol_t **__fh) + efi_file_protocol_t **fh) { - efi_simple_file_system_protocol_t *io; - efi_file_protocol_t *fh; efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; + efi_simple_file_system_protocol_t *io; efi_status_t status; - efi_handle_t handle = image->device_handle; - status = efi_bs_call(handle_protocol, handle, &fs_proto, (void **)&io); + status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto, + (void **)&io); if (status != EFI_SUCCESS) { - efi_printk("Failed to handle fs_proto\n"); + pr_efi_err("Failed to handle fs_proto\n"); return status; } - status = io->open_volume(io, &fh); + status = io->open_volume(io, fh); if (status != EFI_SUCCESS) - efi_printk("Failed to open volume\n"); - else - *__fh = fh; + pr_efi_err("Failed to open volume\n"); return status; } +static int find_file_option(const efi_char16_t *cmdline, int cmdline_len, + const efi_char16_t *prefix, int prefix_size, + efi_char16_t *result, int result_len) +{ + int prefix_len = prefix_size / 2; + bool found = false; + int i; + + for (i = prefix_len; i < cmdline_len; i++) { + if (!memcmp(&cmdline[i - prefix_len], prefix, prefix_size)) { + found = true; + break; + } + } + + if (!found) + return 0; + + while (--result_len > 0 && i < cmdline_len) { + if (cmdline[i] == L'\0' || + cmdline[i] == L'\n' || + cmdline[i] == L' ') + break; + *result++ = cmdline[i++]; + } + *result = L'\0'; + return i; +} + /* * Check the cmdline for a LILO-style file= arguments. * * We only support loading a file from the same filesystem as * the kernel image. */ -efi_status_t handle_cmdline_files(efi_loaded_image_t *image, - char *cmd_line, char *option_string, - unsigned long max_addr, - unsigned long *load_addr, - unsigned long *load_size) +static efi_status_t handle_cmdline_files(efi_loaded_image_t *image, + const efi_char16_t *optstr, + int optstr_size, + unsigned long max_addr, + unsigned long *load_addr, + unsigned long *load_size) { + const efi_char16_t *cmdline = image->load_options; + int cmdline_len = image->load_options_size / 2; unsigned long efi_chunk_size = ULONG_MAX; - struct file_info *files; - unsigned long file_addr; - u64 file_size_total; - efi_file_protocol_t *fh = NULL; + efi_file_protocol_t *volume = NULL; + efi_file_protocol_t *file; + unsigned long alloc_addr; + unsigned long alloc_size; efi_status_t status; - int nr_files; - char *str; - int i, j, k; - - if (IS_ENABLED(CONFIG_X86) && !nochunk()) - efi_chunk_size = EFI_READ_CHUNK_SIZE; - - file_addr = 0; - file_size_total = 0; - - str = cmd_line; - - j = 0; /* See close_handles */ + int offset; if (!load_addr || !load_size) return EFI_INVALID_PARAMETER; - *load_addr = 0; - *load_size = 0; + if (IS_ENABLED(CONFIG_X86) && !nochunk()) + efi_chunk_size = EFI_READ_CHUNK_SIZE; - if (!str || !*str) - return EFI_SUCCESS; + alloc_addr = alloc_size = 0; + do { + efi_char16_t filename[MAX_FILENAME_SIZE]; + unsigned long size; + void *addr; - for (nr_files = 0; *str; nr_files++) { - str = strstr(str, option_string); - if (!str) + offset = find_file_option(cmdline, cmdline_len, + optstr, optstr_size, + filename, ARRAY_SIZE(filename)); + + if (!offset) break; - str += strlen(option_string); + cmdline += offset; + cmdline_len -= offset; - /* Skip any leading slashes */ - while (*str == '/' || *str == '\\') - str++; - - while (*str && *str != ' ' && *str != '\n') - str++; - } - - if (!nr_files) - return EFI_SUCCESS; - - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, - nr_files * sizeof(*files), (void **)&files); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to alloc mem for file handle list\n"); - goto fail; - } - - str = cmd_line; - for (i = 0; i < nr_files; i++) { - struct file_info *file; - efi_char16_t filename_16[256]; - efi_char16_t *p; - - str = strstr(str, option_string); - if (!str) - break; - - str += strlen(option_string); - - file = &files[i]; - p = filename_16; - - /* Skip any leading slashes */ - while (*str == '/' || *str == '\\') - str++; - - while (*str && *str != ' ' && *str != '\n') { - if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) - break; - - if (*str == '/') { - *p++ = '\\'; - str++; - } else { - *p++ = *str++; - } - } - - *p = '\0'; - - /* Only open the volume once. */ - if (!i) { - status = efi_open_volume(image, &fh); + if (!volume) { + status = efi_open_volume(image, &volume); if (status != EFI_SUCCESS) - goto free_files; + return status; } - status = efi_file_size(fh, filename_16, (void **)&file->handle, - &file->size); + status = efi_open_file(volume, filename, &file, &size); if (status != EFI_SUCCESS) - goto close_handles; - - file_size_total += file->size; - } - - if (file_size_total) { - unsigned long addr; + goto err_close_volume; /* - * Multiple files need to be at consecutive addresses in memory, - * so allocate enough memory for all the files. This is used - * for loading multiple files. + * Check whether the existing allocation can contain the next + * file. This condition will also trigger naturally during the + * first (and typically only) iteration of the loop, given that + * alloc_size == 0 in that case. */ - status = efi_allocate_pages(file_size_total, &file_addr, max_addr); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to alloc highmem for files\n"); - goto close_handles; - } + if (round_up(alloc_size + size, EFI_ALLOC_ALIGN) > + round_up(alloc_size, EFI_ALLOC_ALIGN)) { + unsigned long old_addr = alloc_addr; - /* We've run out of free low memory. */ - if (file_addr > max_addr) { - pr_efi_err("We've run out of free low memory\n"); - status = EFI_INVALID_PARAMETER; - goto free_file_total; - } - - addr = file_addr; - for (j = 0; j < nr_files; j++) { - unsigned long size; - - size = files[j].size; - while (size) { - unsigned long chunksize; - - if (size > efi_chunk_size) - chunksize = efi_chunk_size; - else - chunksize = size; - - status = efi_file_read(files[j].handle, - &chunksize, - (void *)addr); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to read file\n"); - goto free_file_total; - } - addr += chunksize; - size -= chunksize; + status = efi_allocate_pages(alloc_size + size, &alloc_addr, + max_addr); + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to reallocate memory for files\n"); + goto err_close_file; } - efi_file_close(files[j].handle); + if (old_addr != 0) { + /* + * This is not the first time we've gone + * around this loop, and so we are loading + * multiple files that need to be concatenated + * and returned in a single buffer. + */ + memcpy((void *)alloc_addr, (void *)old_addr, alloc_size); + efi_free(alloc_size, old_addr); + } } - } + addr = (void *)alloc_addr + alloc_size; + alloc_size += size; - efi_bs_call(free_pool, files); + while (size) { + unsigned long chunksize = min(size, efi_chunk_size); - *load_addr = file_addr; - *load_size = file_size_total; + status = file->read(file, &chunksize, addr); + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to read file\n"); + goto err_close_file; + } + addr += chunksize; + size -= chunksize; + } + file->close(file); + } while (offset > 0); - return status; + *load_addr = alloc_addr; + *load_size = alloc_size; -free_file_total: - efi_free(file_size_total, file_addr); + if (volume) + volume->close(volume); + return EFI_SUCCESS; -close_handles: - for (k = j; k < i; k++) - efi_file_close(files[k].handle); -free_files: - efi_bs_call(free_pool, files); -fail: - *load_addr = 0; - *load_size = 0; +err_close_file: + file->close(file); +err_close_volume: + volume->close(volume); + efi_free(alloc_size, alloc_addr); return status; } + +efi_status_t efi_load_dtb(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size) +{ + return handle_cmdline_files(image, L"dtb=", sizeof(L"dtb=") - 2, + ULONG_MAX, load_addr, load_size); +} + +efi_status_t efi_load_initrd(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size, + unsigned long max_addr) +{ + return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, + max_addr, load_addr, load_size); +} diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index f2dbf119837c..39d04735f1c5 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -421,18 +421,14 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, if (status != EFI_SUCCESS) goto fail2; - status = handle_cmdline_files(image, - (char *)(unsigned long)hdr->cmd_line_ptr, - "initrd=", hdr->initrd_addr_max, - &ramdisk_addr, &ramdisk_size); + status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size, + hdr->initrd_addr_max); if (status != EFI_SUCCESS && hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) { efi_printk("Trying to load files to higher address\n"); - status = handle_cmdline_files(image, - (char *)(unsigned long)hdr->cmd_line_ptr, - "initrd=", -1UL, - &ramdisk_addr, &ramdisk_size); + status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size, + ULONG_MAX); } if (status != EFI_SUCCESS) From 31f5e546655b502d508e160e6cab20462089e34d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:45 +0100 Subject: [PATCH 0491/2231] efi/libstub: Take soft and hard memory limits into account for initrd loading On x86, the preferred load address of the initrd is still below 4 GB, even though in some cases, we can cope with an initrd that is loaded above that. To simplify the code, and to make it more straightforward to introduce other ways to load the initrd, pass the soft and hard memory limits at the same time, and let the code handling the initrd= command line option deal with this. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm-stub.c | 2 +- drivers/firmware/efi/libstub/efistub.h | 3 ++- drivers/firmware/efi/libstub/file.c | 21 +++++++++++++++------ drivers/firmware/efi/libstub/x86-stub.c | 11 ++--------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index ad44783ae128..a11257a90dd0 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -267,7 +267,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!fdt_addr) pr_efi("Generating empty DTB\n"); - status = efi_load_initrd(image, &initrd_addr, &initrd_size, + status = efi_load_initrd(image, &initrd_addr, &initrd_size, ULONG_MAX, efi_get_max_initrd_addr(dram_base, image_addr)); if (status != EFI_SUCCESS) pr_efi_err("Failed initrd from command line!\n"); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 60d929469b8b..d282d907cd33 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -619,6 +619,7 @@ efi_status_t efi_load_dtb(efi_loaded_image_t *image, efi_status_t efi_load_initrd(efi_loaded_image_t *image, unsigned long *load_addr, unsigned long *load_size, - unsigned long max_addr); + unsigned long soft_limit, + unsigned long hard_limit); #endif diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index 0d67432ed067..be78f64f8d80 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -123,7 +123,8 @@ static int find_file_option(const efi_char16_t *cmdline, int cmdline_len, static efi_status_t handle_cmdline_files(efi_loaded_image_t *image, const efi_char16_t *optstr, int optstr_size, - unsigned long max_addr, + unsigned long soft_limit, + unsigned long hard_limit, unsigned long *load_addr, unsigned long *load_size) { @@ -179,8 +180,15 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image, round_up(alloc_size, EFI_ALLOC_ALIGN)) { unsigned long old_addr = alloc_addr; - status = efi_allocate_pages(alloc_size + size, &alloc_addr, - max_addr); + status = EFI_OUT_OF_RESOURCES; + if (soft_limit < hard_limit) + status = efi_allocate_pages(alloc_size + size, + &alloc_addr, + soft_limit); + if (status == EFI_OUT_OF_RESOURCES) + status = efi_allocate_pages(alloc_size + size, + &alloc_addr, + hard_limit); if (status != EFI_SUCCESS) { pr_efi_err("Failed to reallocate memory for files\n"); goto err_close_file; @@ -236,14 +244,15 @@ efi_status_t efi_load_dtb(efi_loaded_image_t *image, unsigned long *load_size) { return handle_cmdline_files(image, L"dtb=", sizeof(L"dtb=") - 2, - ULONG_MAX, load_addr, load_size); + ULONG_MAX, ULONG_MAX, load_addr, load_size); } efi_status_t efi_load_initrd(efi_loaded_image_t *image, unsigned long *load_addr, unsigned long *load_size, - unsigned long max_addr) + unsigned long soft_limit, + unsigned long hard_limit) { return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, - max_addr, load_addr, load_size); + soft_limit, hard_limit, load_addr, load_size); } diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 39d04735f1c5..52a1a2df2071 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -422,15 +422,8 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, goto fail2; status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size, - hdr->initrd_addr_max); - - if (status != EFI_SUCCESS && - hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) { - efi_printk("Trying to load files to higher address\n"); - status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size, - ULONG_MAX); - } - + hdr->initrd_addr_max, + above4g ? ULONG_MAX : hdr->initrd_addr_max); if (status != EFI_SUCCESS) goto fail2; hdr->ramdisk_image = ramdisk_addr & 0xffffffff; From 91d150c0cc637b9d9d6394936add7cd2b7ccc410 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:46 +0100 Subject: [PATCH 0492/2231] efi/libstub: Clean up command line parsing routine We currently parse the command non-destructively, to avoid having to allocate memory for a copy before passing it to the standard parsing routines that are used by the core kernel, and which modify the input to delineate the parsed tokens with NUL characters. Instead, we call strstr() and strncmp() to go over the input multiple times, and match prefixes rather than tokens, which implies that we would match, e.g., 'nokaslrfoo' in the stub and disable KASLR, while the kernel would disregard the option and run with KASLR enabled. In order to avoid having to reason about whether and how this behavior may be abused, let's clean up the parsing routines, and rebuild them on top of the existing helpers. Signed-off-by: Ard Biesheuvel --- arch/arm64/kernel/image-vars.h | 1 + drivers/firmware/efi/libstub/Makefile | 3 +- .../firmware/efi/libstub/efi-stub-helper.c | 79 ++++++------------- drivers/firmware/efi/libstub/skip_spaces.c | 11 +++ drivers/firmware/efi/libstub/string.c | 56 +++++++++++++ drivers/firmware/efi/libstub/x86-stub.c | 2 +- 6 files changed, 97 insertions(+), 55 deletions(-) create mode 100644 drivers/firmware/efi/libstub/skip_spaces.c diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index 87cb3d45b4bd..9a7aef0d6f70 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -47,6 +47,7 @@ __efistub_stext = stext; __efistub__end = _end; __efistub__edata = _edata; __efistub_screen_info = screen_info; +__efistub__ctype = _ctype; #endif diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 1202c9ee0ea9..4d6246c6f651 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -40,7 +40,8 @@ OBJECT_FILES_NON_STANDARD := y KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ - file.o mem.o random.o randomalloc.o pci.o + file.o mem.o random.o randomalloc.o pci.o \ + skip_spaces.o lib-cmdline.o lib-ctype.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index db23be5dc69b..49008ac88b63 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -68,66 +68,39 @@ void efi_printk(char *str) */ efi_status_t efi_parse_options(char const *cmdline) { - char *str; + size_t len = strlen(cmdline) + 1; + efi_status_t status; + char *str, *buf; - str = strstr(cmdline, "nokaslr"); - if (str == cmdline || (str && str > cmdline && *(str - 1) == ' ')) - efi_nokaslr = true; + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, len, (void **)&buf); + if (status != EFI_SUCCESS) + return status; - str = strstr(cmdline, "quiet"); - if (str == cmdline || (str && str > cmdline && *(str - 1) == ' ')) - efi_quiet = true; + str = skip_spaces(memcpy(buf, cmdline, len)); - /* - * If no EFI parameters were specified on the cmdline we've got - * nothing to do. - */ - str = strstr(cmdline, "efi="); - if (!str) - return EFI_SUCCESS; + while (*str) { + char *param, *val; - /* Skip ahead to first argument */ - str += strlen("efi="); + str = next_arg(str, ¶m, &val); - /* - * Remember, because efi= is also used by the kernel we need to - * skip over arguments we don't understand. - */ - while (*str && *str != ' ') { - if (!strncmp(str, "nochunk", 7)) { - str += strlen("nochunk"); - efi_nochunk = true; + if (!strcmp(param, "nokaslr")) { + efi_nokaslr = true; + } else if (!strcmp(param, "quiet")) { + efi_quiet = true; + } else if (!strcmp(param, "efi") && val) { + efi_nochunk = parse_option_str(val, "nochunk"); + efi_novamap = parse_option_str(val, "novamap"); + + efi_nosoftreserve = IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && + parse_option_str(val, "nosoftreserve"); + + if (parse_option_str(val, "disable_early_pci_dma")) + efi_disable_pci_dma = true; + if (parse_option_str(val, "no_disable_early_pci_dma")) + efi_disable_pci_dma = false; } - - if (!strncmp(str, "novamap", 7)) { - str += strlen("novamap"); - efi_novamap = true; - } - - if (IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && - !strncmp(str, "nosoftreserve", 7)) { - str += strlen("nosoftreserve"); - efi_nosoftreserve = true; - } - - if (!strncmp(str, "disable_early_pci_dma", 21)) { - str += strlen("disable_early_pci_dma"); - efi_disable_pci_dma = true; - } - - if (!strncmp(str, "no_disable_early_pci_dma", 24)) { - str += strlen("no_disable_early_pci_dma"); - efi_disable_pci_dma = false; - } - - /* Group words together, delimited by "," */ - while (*str && *str != ' ' && *str != ',') - str++; - - if (*str == ',') - str++; } - + efi_bs_call(free_pool, buf); return EFI_SUCCESS; } diff --git a/drivers/firmware/efi/libstub/skip_spaces.c b/drivers/firmware/efi/libstub/skip_spaces.c new file mode 100644 index 000000000000..a700b3c7f7d0 --- /dev/null +++ b/drivers/firmware/efi/libstub/skip_spaces.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +char *skip_spaces(const char *str) +{ + while (isspace(*str)) + ++str; + return (char *)str; +} diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c index ed10e3f602c5..1ac2f8764715 100644 --- a/drivers/firmware/efi/libstub/string.c +++ b/drivers/firmware/efi/libstub/string.c @@ -6,6 +6,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#include #include #include @@ -56,3 +57,58 @@ int strncmp(const char *cs, const char *ct, size_t count) return 0; } #endif + +/* Works only for digits and letters, but small and fast */ +#define TOLOWER(x) ((x) | 0x20) + +static unsigned int simple_guess_base(const char *cp) +{ + if (cp[0] == '0') { + if (TOLOWER(cp[1]) == 'x' && isxdigit(cp[2])) + return 16; + else + return 8; + } else { + return 10; + } +} + +/** + * simple_strtoull - convert a string to an unsigned long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ + +unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) +{ + unsigned long long result = 0; + + if (!base) + base = simple_guess_base(cp); + + if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x') + cp += 2; + + while (isxdigit(*cp)) { + unsigned int value; + + value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10; + if (value >= base) + break; + result = result * base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + + return result; +} + +long simple_strtol(const char *cp, char **endp, unsigned int base) +{ + if (*cp == '-') + return -simple_strtoull(cp + 1, endp, base); + + return simple_strtoull(cp, endp, base); +} diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 52a1a2df2071..681b620d8d40 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -726,7 +726,7 @@ struct boot_params *efi_main(efi_handle_t handle, hdr->code32_start = (u32)bzimage_addr; /* - * make_boot_params() may have been called before efi_main(), in which + * efi_pe_entry() may have been called before efi_main(), in which * case this is the second time we parse the cmdline. This is ok, * parsing the cmdline multiple times does not have side-effects. */ From abd268685a21cb5d0c991bb21a88ea0c1d2e15d8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:47 +0100 Subject: [PATCH 0493/2231] efi/libstub: Expose LocateDevicePath boot service We will be adding support for loading the initrd from a GUIDed device path in a subsequent patch, so update the prototype of the LocateDevicePath() boot service to make it callable from our code. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 3 +++ drivers/firmware/efi/libstub/efistub.h | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 85f79accd3f8..9ced980b123b 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -283,6 +283,9 @@ static inline void *efi64_zero_upper(void *p) #define __efi64_argmap_locate_protocol(protocol, reg, interface) \ ((protocol), (reg), efi64_zero_upper(interface)) +#define __efi64_argmap_locate_device_path(protocol, path, handle) \ + ((protocol), (path), efi64_zero_upper(handle)) + /* PCI I/O */ #define __efi64_argmap_get_location(protocol, seg, bus, dev, func) \ ((protocol), efi64_zero_upper(seg), efi64_zero_upper(bus), \ diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index d282d907cd33..afa774a312f5 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -100,6 +100,8 @@ struct efi_boot_memmap { unsigned long *buff_size; }; +typedef struct efi_generic_dev_path efi_device_path_protocol_t; + /* * EFI Boot Services table */ @@ -134,7 +136,9 @@ union efi_boot_services { efi_status_t (__efiapi *locate_handle)(int, efi_guid_t *, void *, unsigned long *, efi_handle_t *); - void *locate_device_path; + efi_status_t (__efiapi *locate_device_path)(efi_guid_t *, + efi_device_path_protocol_t **, + efi_handle_t *); efi_status_t (__efiapi *install_configuration_table)(efi_guid_t *, void *); void *load_image; From 2931d526d5674940d916a4b513a681ee3562e574 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:48 +0100 Subject: [PATCH 0494/2231] efi/libstub: Make the LoadFile EFI protocol accessible Add the protocol definitions, GUIDs and mixed mode glue so that the EFI loadfile protocol can be used from the stub. This will be used in a future patch to load the initrd. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 4 ++++ drivers/firmware/efi/libstub/efistub.h | 14 ++++++++++++++ include/linux/efi.h | 2 ++ 3 files changed, 20 insertions(+) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 9ced980b123b..fcb21e3d13c5 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -291,6 +291,10 @@ static inline void *efi64_zero_upper(void *p) ((protocol), efi64_zero_upper(seg), efi64_zero_upper(bus), \ efi64_zero_upper(dev), efi64_zero_upper(func)) +/* LoadFile */ +#define __efi64_argmap_load_file(protocol, path, policy, bufsize, buf) \ + ((protocol), (path), (policy), efi64_zero_upper(bufsize), (buf)) + /* * The macros below handle the plumbing for the argument mapping. To add a * mapping for a specific EFI method, simply define a macro diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index afa774a312f5..34fe3fad042f 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -541,6 +541,20 @@ union efi_tcg2_protocol { } mixed_mode; }; +typedef union efi_load_file_protocol efi_load_file_protocol_t; +typedef union efi_load_file_protocol efi_load_file2_protocol_t; + +union efi_load_file_protocol { + struct { + efi_status_t (__efiapi *load_file)(efi_load_file_protocol_t *, + efi_device_path_protocol_t *, + bool, unsigned long *, void *); + }; + struct { + u32 load_file; + } mixed_mode; +}; + void efi_pci_disable_bridge_busmaster(void); typedef efi_status_t (*efi_exit_boot_map_processing)( diff --git a/include/linux/efi.h b/include/linux/efi.h index 0e047d2738cd..9ccf313fe9de 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -332,6 +332,8 @@ void efi_native_runtime_setup(void); #define EFI_CONSOLE_OUT_DEVICE_GUID EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) #define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0) #define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f) +#define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d) #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) From eaa6fc67e11c13e00799af9ca70d9fa3725c85e7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 16 Feb 2020 18:13:40 +0100 Subject: [PATCH 0495/2231] efi/libstub: Add function description of efi_allocate_pages() Provide a Sphinx style function description for efi_allocate_pages(). Signed-off-by: Heinrich Schuchardt Link: https://lore.kernel.org/r/20200216171340.6070-1-xypron.glpk@gmx.de Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/mem.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 5808c8764e64..17060086913a 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -65,8 +65,20 @@ fail: return status; } -/* - * Allocate at the highest possible address that is not above 'max'. +/** + * efi_allocate_pages() - Allocate memory pages + * @size: minimum number of bytes to allocate + * @addr: On return the address of the first allocated page. The first + * allocated page has alignment EFI_ALLOC_ALIGN which is an + * architecture dependent multiple of the page size. + * @max: the address that the last allocated memory page shall not + * exceed + * + * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according + * to EFI_ALLOC_ALIGN. The last allocated page will not exceed the address + * given by @max. + * + * Return: status code */ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, unsigned long max) From e7ea37b00da43e8f1154b35ef7f1e3aff45981d3 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 16 Feb 2020 19:40:50 +0100 Subject: [PATCH 0496/2231] efi/libstub: Simplify efi_get_memory_map() Do not check the value of status twice. Signed-off-by: Heinrich Schuchardt Link: https://lore.kernel.org/r/20200216184050.3100-1-xypron.glpk@gmx.de Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/mem.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 17060086913a..2499e0485081 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -52,13 +52,14 @@ again: goto again; } - if (status != EFI_SUCCESS) + if (status == EFI_SUCCESS) { + if (map->key_ptr) + *map->key_ptr = key; + if (map->desc_ver) + *map->desc_ver = desc_version; + } else { efi_bs_call(free_pool, m); - - if (map->key_ptr && status == EFI_SUCCESS) - *map->key_ptr = key; - if (map->desc_ver && status == EFI_SUCCESS) - *map->desc_ver = desc_version; + } fail: *map->map = m; From 1d9b17683547348341b67705c578a1ca1882fd23 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 18 Feb 2020 07:30:38 +0100 Subject: [PATCH 0497/2231] efi/libstub: Describe memory functions Provide descriptions of: * efi_get_memory_map() * efi_low_alloc_above() * efi_free() Signed-off-by: Heinrich Schuchardt Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20200218063038.3436-1-xypron.glpk@gmx.de Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/mem.c | 36 ++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 2499e0485081..94b029965f41 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -16,6 +16,15 @@ static inline bool mmap_has_headroom(unsigned long buff_size, return slack / desc_size >= EFI_MMAP_NR_SLACK_SLOTS; } +/** + * efi_get_memory_map() - get memory map + * @map: on return pointer to memory map + * + * Retrieve the UEFI memory map. The allocated memory leaves room for + * up to EFI_MMAP_NR_SLACK_SLOTS additional memory map entries. + * + * Return: status code + */ efi_status_t efi_get_memory_map(struct efi_boot_memmap *map) { efi_memory_desc_t *m = NULL; @@ -109,8 +118,20 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, } return EFI_SUCCESS; } -/* - * Allocate at the lowest possible address that is not below 'min'. +/** + * efi_low_alloc_above() - allocate pages at or above given address + * @size: size of the memory area to allocate + * @align: minimum alignment of the allocated memory area. It should + * a power of two. + * @addr: on exit the address of the allocated memory + * @min: minimum address to used for the memory allocation + * + * Allocate at the lowest possible address that is not below @min as + * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at + * least EFI_ALLOC_ALIGN. The first allocated page will not below the address + * given by @min. + * + * Return: status code */ efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, unsigned long *addr, unsigned long min) @@ -187,6 +208,17 @@ fail: return status; } +/** + * efi_free() - free memory pages + * @size: size of the memory area to free in bytes + * @addr: start of the memory area to free (must be EFI_PAGE_SIZE + * aligned) + * + * @size is rounded up to a multiple of EFI_ALLOC_ALIGN which is an + * architecture specific multiple of EFI_PAGE_SIZE. So this function should + * only be used to return pages allocated with efi_allocate_pages() or + * efi_low_alloc_above(). + */ void efi_free(unsigned long size, unsigned long addr) { unsigned long nr_pages; From 0255973bd6e471e1c34284328098bfab89840df3 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 20 Feb 2020 07:53:17 +0100 Subject: [PATCH 0498/2231] efi/libstub: Describe efi_relocate_kernel() Update the description of of efi_relocate_kernel() to match Sphinx style. Update parameter references in the description of other memory functions to use @param style. Signed-off-by: Heinrich Schuchardt Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20200220065317.9096-1-xypron.glpk@gmx.de Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/mem.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 94b029965f41..869a79c8946f 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -230,15 +230,26 @@ void efi_free(unsigned long size, unsigned long addr) efi_bs_call(free_pages, addr, nr_pages); } -/* - * Relocate a kernel image, either compressed or uncompressed. - * In the ARM64 case, all kernel images are currently - * uncompressed, and as such when we relocate it we need to - * allocate additional space for the BSS segment. Any low - * memory that this function should avoid needs to be - * unavailable in the EFI memory map, as if the preferred - * address is not available the lowest available address will - * be used. +/** + * efi_relocate_kernel() - copy memory area + * @image_addr: pointer to address of memory area to copy + * @image_size: size of memory area to copy + * @alloc_size: minimum size of memory to allocate, must be greater or + * equal to image_size + * @preferred_addr: preferred target address + * @alignment: minimum alignment of the allocated memory area. It + * should be a power of two. + * @min_addr: minimum target address + * + * Copy a memory area to a newly allocated memory area aligned according + * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address + * is not available, the allocated address will not be below @min_addr. + * On exit, @image_addr is updated to the target copy address that was used. + * + * This function is used to copy the Linux kernel verbatim. It does not apply + * any relocation changes. + * + * Return: status code */ efi_status_t efi_relocate_kernel(unsigned long *image_addr, unsigned long image_size, From ba832f68dcf171c5d4439e763d81d14a149355a9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 21 Feb 2020 12:47:16 +0100 Subject: [PATCH 0499/2231] efi/libstub: Describe RNG functions Provide descriptions for the functions invoking the EFI_RNG_PROTOCOL. Signed-off-by: Heinrich Schuchardt Reviewed-by: Dominik Brodowski Link: https://lore.kernel.org/r/20200221114716.4372-1-xypron.glpk@gmx.de Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/random.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index 21e7e9325219..24aa37535372 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -25,6 +25,17 @@ union efi_rng_protocol { } mixed_mode; }; +/** + * efi_get_random_bytes() - fill a buffer with random bytes + * @size: size of the buffer + * @out: caller allocated buffer to receive the random bytes + * + * The call will fail if either the firmware does not implement the + * EFI_RNG_PROTOCOL or there are not enough random bytes available to fill + * the buffer. + * + * Return: status code + */ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out) { efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; @@ -38,6 +49,19 @@ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out) return efi_call_proto(rng, get_rng, NULL, size, out); } +/** + * efi_random_get_seed() - provide random seed as configuration table + * + * The EFI_RNG_PROTOCOL is used to read random bytes. These random bytes are + * saved as a configuration table which can be used as entropy by the kernel + * for the initialization of its pseudo random number generator. + * + * If the EFI_RNG_PROTOCOL is not available or there are not enough random bytes + * available, the configuration table will not be installed and an error code + * will be returned. + * + * Return: status code + */ efi_status_t efi_random_get_seed(void) { efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; From f01dd5b3d1f4fad9ad4f309632f51ed940991dab Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 21 Feb 2020 20:18:29 +0100 Subject: [PATCH 0500/2231] efi/libstub: Fix error message in handle_cmdline_files() The memory for files is allocated not reallocated. Signed-off-by: Heinrich Schuchardt Link: https://lore.kernel.org/r/20200221191829.18149-1-xypron.glpk@gmx.de Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index be78f64f8d80..d4c7e5f59d2c 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -190,7 +190,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image, &alloc_addr, hard_limit); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to reallocate memory for files\n"); + pr_efi_err("Failed to allocate memory for files\n"); goto err_close_file; } From 6d2576e4805ae26ac7a64ea1d3be28c18d5c99b5 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 23 Feb 2020 21:45:57 +0100 Subject: [PATCH 0501/2231] efi/esrt: Clean up efi_esrt_init Remove an unused variable in __init efi_esrt_init(). Simplify a logical constraint. Signed-off-by: Heinrich Schuchardt Link: https://lore.kernel.org/r/20200223204557.114634-1-xypron.glpk@gmx.de Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/esrt.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c index 2762e0662bf4..e3d692696583 100644 --- a/drivers/firmware/efi/esrt.c +++ b/drivers/firmware/efi/esrt.c @@ -240,7 +240,6 @@ void __init efi_esrt_init(void) { void *va; struct efi_system_resource_table tmpesrt; - struct efi_system_resource_entry_v1 *v1_entries; size_t size, max, entry_size, entries_size; efi_memory_desc_t md; int rc; @@ -288,14 +287,13 @@ void __init efi_esrt_init(void) memcpy(&tmpesrt, va, sizeof(tmpesrt)); early_memunmap(va, size); - if (tmpesrt.fw_resource_version == 1) { - entry_size = sizeof (*v1_entries); - } else { + if (tmpesrt.fw_resource_version != 1) { pr_err("Unsupported ESRT version %lld.\n", tmpesrt.fw_resource_version); return; } + entry_size = sizeof(struct efi_system_resource_entry_v1); if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) { pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n", max - size, entry_size); From e0dc26c781fb69248add2b33549461cd780165b8 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 23 Feb 2020 21:54:35 +0100 Subject: [PATCH 0502/2231] efi/capsule-loader: Drop superfluous assignment In efi_capsule_write() the value 0 assigned to ret is never used. Identified with cppcheck. Signed-off-by: Heinrich Schuchardt Link: https://lore.kernel.org/r/20200223205435.114915-1-xypron.glpk@gmx.de Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/capsule-loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c index d3067cbd5114..4dde8edd53b6 100644 --- a/drivers/firmware/efi/capsule-loader.c +++ b/drivers/firmware/efi/capsule-loader.c @@ -168,7 +168,7 @@ static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info) static ssize_t efi_capsule_write(struct file *file, const char __user *buff, size_t count, loff_t *offp) { - int ret = 0; + int ret; struct capsule_info *cap_info = file->private_data; struct page *page; void *kbuff = NULL; From 14b60cc8e0ead866cd25b4e51945c6267ca9936c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 2 Feb 2020 11:30:14 +0100 Subject: [PATCH 0503/2231] efi/x86: Reindent struct initializer for legibility Reindent the efi_memory_map_data initializer so that all the = signs are aligned vertically, making the resulting code much easier to read. Suggested-by: Ingo Molnar Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index ae923ee8e2b4..293c47f9cb39 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -305,11 +305,11 @@ static void __init efi_clean_memmap(void) if (n_removal > 0) { struct efi_memory_map_data data = { - .phys_map = efi.memmap.phys_map, - .desc_version = efi.memmap.desc_version, - .desc_size = efi.memmap.desc_size, - .size = efi.memmap.desc_size * (efi.memmap.nr_map - n_removal), - .flags = 0, + .phys_map = efi.memmap.phys_map, + .desc_version = efi.memmap.desc_version, + .desc_size = efi.memmap.desc_size, + .size = efi.memmap.desc_size * (efi.memmap.nr_map - n_removal), + .flags = 0, }; pr_warn("Removing %d invalid memory map entries.\n", n_removal); From a570b0624b3f73a6cc57c529e506300c639912e2 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 2 Feb 2020 11:22:41 +0100 Subject: [PATCH 0504/2231] efi/x86: Replace #ifdefs with IS_ENABLED() checks When possible, IS_ENABLED() conditionals are preferred over #ifdefs, given that the latter hide the code from the compiler entirely, which reduces build test coverage when the option is not enabled. So replace an instance in the x86 efi startup code. Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 293c47f9cb39..52067ed7fd59 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -214,16 +214,13 @@ int __init efi_memblock_x86_reserve_range(void) if (efi_enabled(EFI_PARAVIRT)) return 0; -#ifdef CONFIG_X86_32 - /* Can't handle data above 4GB at this time */ - if (e->efi_memmap_hi) { + /* Can't handle firmware tables above 4GB on i386 */ + if (IS_ENABLED(CONFIG_X86_32) && e->efi_memmap_hi > 0) { pr_err("Memory map is above 4GB, disabling EFI.\n"); return -EINVAL; } - pmap = e->efi_memmap; -#else - pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32)); -#endif + pmap = (phys_addr_t)(e->efi_memmap | ((u64)e->efi_memmap_hi << 32)); + data.phys_map = pmap; data.size = e->efi_memmap_size; data.desc_size = e->efi_memdesc_size; From db8952e7094fde3a397321240d5d57ec111258d8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 08:46:57 +0000 Subject: [PATCH 0505/2231] efi/dev-path-parser: Add struct definition for vendor type device path nodes In preparation of adding support for loading the initrd via a special device path, add the struct definition of a vendor GUIDed device path node to efi.h. Since we will be producing these data structures rather than just consumsing the ones instantiated by the firmware, refactor the various device path node definitions so we can take the size of each node using sizeof() rather than having to resort to opaque arithmetic in the static initializers. While at it, drop the #if IS_ENABLED() check for the declaration of efi_get_device_by_path(), which is unnecessary, and constify its first argument as well. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/apple-properties.c | 8 ++--- drivers/firmware/efi/dev-path-parser.c | 38 ++++++++++---------- include/linux/efi.h | 48 +++++++++++++++---------- 3 files changed, 52 insertions(+), 42 deletions(-) diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c index 084942846f4d..34f53d898acb 100644 --- a/drivers/firmware/efi/apple-properties.c +++ b/drivers/firmware/efi/apple-properties.c @@ -46,7 +46,7 @@ struct properties_header { }; static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, - struct device *dev, void *ptr, + struct device *dev, const void *ptr, struct property_entry entry[]) { int i; @@ -117,10 +117,10 @@ static int __init unmarshal_devices(struct properties_header *properties) while (offset + sizeof(struct dev_header) < properties->len) { struct dev_header *dev_header = (void *)properties + offset; struct property_entry *entry = NULL; + const struct efi_dev_path *ptr; struct device *dev; size_t len; int ret, i; - void *ptr; if (offset + dev_header->len > properties->len || dev_header->len <= sizeof(*dev_header)) { @@ -131,10 +131,10 @@ static int __init unmarshal_devices(struct properties_header *properties) ptr = dev_header->path; len = dev_header->len - sizeof(*dev_header); - dev = efi_get_device_by_path((struct efi_dev_path **)&ptr, &len); + dev = efi_get_device_by_path(&ptr, &len); if (IS_ERR(dev)) { pr_err("device path parse error %ld at %#zx:\n", - PTR_ERR(dev), ptr - (void *)dev_header); + PTR_ERR(dev), (void *)ptr - (void *)dev_header); print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET, 16, 1, dev_header, dev_header->len, true); dev = NULL; diff --git a/drivers/firmware/efi/dev-path-parser.c b/drivers/firmware/efi/dev-path-parser.c index 20123384271c..5c9625e552f4 100644 --- a/drivers/firmware/efi/dev-path-parser.c +++ b/drivers/firmware/efi/dev-path-parser.c @@ -31,13 +31,13 @@ static int __init match_acpi_dev(struct device *dev, const void *data) return !strcmp("0", hid_uid.uid); } -static long __init parse_acpi_path(struct efi_dev_path *node, +static long __init parse_acpi_path(const struct efi_dev_path *node, struct device *parent, struct device **child) { struct acpi_hid_uid hid_uid = {}; struct device *phys_dev; - if (node->length != 12) + if (node->header.length != 12) return -EINVAL; sprintf(hid_uid.hid[0].id, "%c%c%c%04X", @@ -69,12 +69,12 @@ static int __init match_pci_dev(struct device *dev, void *data) return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn; } -static long __init parse_pci_path(struct efi_dev_path *node, +static long __init parse_pci_path(const struct efi_dev_path *node, struct device *parent, struct device **child) { unsigned int devfn; - if (node->length != 6) + if (node->header.length != 6) return -EINVAL; if (!parent) return -EINVAL; @@ -105,19 +105,19 @@ static long __init parse_pci_path(struct efi_dev_path *node, * search for a device. */ -static long __init parse_end_path(struct efi_dev_path *node, +static long __init parse_end_path(const struct efi_dev_path *node, struct device *parent, struct device **child) { - if (node->length != 4) + if (node->header.length != 4) return -EINVAL; - if (node->sub_type != EFI_DEV_END_INSTANCE && - node->sub_type != EFI_DEV_END_ENTIRE) + if (node->header.sub_type != EFI_DEV_END_INSTANCE && + node->header.sub_type != EFI_DEV_END_ENTIRE) return -EINVAL; if (!parent) return -ENODEV; *child = get_device(parent); - return node->sub_type; + return node->header.sub_type; } /** @@ -156,7 +156,7 @@ static long __init parse_end_path(struct efi_dev_path *node, * %ERR_PTR(-EINVAL) if a node is malformed or exceeds @len, * %ERR_PTR(-ENOTSUPP) if support for a node type is not yet implemented. */ -struct device * __init efi_get_device_by_path(struct efi_dev_path **node, +struct device * __init efi_get_device_by_path(const struct efi_dev_path **node, size_t *len) { struct device *parent = NULL, *child; @@ -166,16 +166,16 @@ struct device * __init efi_get_device_by_path(struct efi_dev_path **node, return NULL; while (!ret) { - if (*len < 4 || *len < (*node)->length) + if (*len < 4 || *len < (*node)->header.length) ret = -EINVAL; - else if ((*node)->type == EFI_DEV_ACPI && - (*node)->sub_type == EFI_DEV_BASIC_ACPI) + else if ((*node)->header.type == EFI_DEV_ACPI && + (*node)->header.sub_type == EFI_DEV_BASIC_ACPI) ret = parse_acpi_path(*node, parent, &child); - else if ((*node)->type == EFI_DEV_HW && - (*node)->sub_type == EFI_DEV_PCI) + else if ((*node)->header.type == EFI_DEV_HW && + (*node)->header.sub_type == EFI_DEV_PCI) ret = parse_pci_path(*node, parent, &child); - else if (((*node)->type == EFI_DEV_END_PATH || - (*node)->type == EFI_DEV_END_PATH2)) + else if (((*node)->header.type == EFI_DEV_END_PATH || + (*node)->header.type == EFI_DEV_END_PATH2)) ret = parse_end_path(*node, parent, &child); else ret = -ENOTSUPP; @@ -185,8 +185,8 @@ struct device * __init efi_get_device_by_path(struct efi_dev_path **node, return ERR_PTR(ret); parent = child; - *node = (void *)*node + (*node)->length; - *len -= (*node)->length; + *node = (void *)*node + (*node)->header.length; + *len -= (*node)->header.length; } if (ret == EFI_DEV_END_ENTIRE) diff --git a/include/linux/efi.h b/include/linux/efi.h index 9ccf313fe9de..0976e57b4caa 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -855,30 +855,40 @@ extern int efi_status_to_err(efi_status_t status); #define EFI_DEV_END_ENTIRE 0xFF struct efi_generic_dev_path { - u8 type; - u8 sub_type; - u16 length; -} __attribute ((packed)); + u8 type; + u8 sub_type; + u16 length; +} __packed; + +struct efi_acpi_dev_path { + struct efi_generic_dev_path header; + u32 hid; + u32 uid; +} __packed; + +struct efi_pci_dev_path { + struct efi_generic_dev_path header; + u8 fn; + u8 dev; +} __packed; + +struct efi_vendor_dev_path { + struct efi_generic_dev_path header; + efi_guid_t vendorguid; + u8 vendordata[]; +} __packed; struct efi_dev_path { - u8 type; /* can be replaced with unnamed */ - u8 sub_type; /* struct efi_generic_dev_path; */ - u16 length; /* once we've moved to -std=c11 */ union { - struct { - u32 hid; - u32 uid; - } acpi; - struct { - u8 fn; - u8 dev; - } pci; + struct efi_generic_dev_path header; + struct efi_acpi_dev_path acpi; + struct efi_pci_dev_path pci; + struct efi_vendor_dev_path vendor; }; -} __attribute ((packed)); +} __packed; -#if IS_ENABLED(CONFIG_EFI_DEV_PATH_PARSER) -struct device *efi_get_device_by_path(struct efi_dev_path **node, size_t *len); -#endif +struct device *efi_get_device_by_path(const struct efi_dev_path **node, + size_t *len); static inline void memrange_efi_to_native(u64 *addr, u64 *npages) { From ec93fc371f014a6fb483e3556061ecad4b40735c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 3 Feb 2020 23:45:14 +0000 Subject: [PATCH 0506/2231] efi/libstub: Add support for loading the initrd from a device path There are currently two ways to specify the initrd to be passed to the Linux kernel when booting via the EFI stub: - it can be passed as a initrd= command line option when doing a pure PE boot (as opposed to the EFI handover protocol that exists for x86) - otherwise, the bootloader or firmware can load the initrd into memory, and pass the address and size via the bootparams struct (x86) or device tree (ARM) In the first case, we are limited to loading from the same file system that the kernel was loaded from, and it is also problematic in a trusted boot context, given that we cannot easily protect the command line from tampering without either adding complicated white/blacklisting of boot arguments or locking down the command line altogether. In the second case, we force the bootloader to duplicate knowledge about the boot protocol which is already encoded in the stub, and which may be subject to change over time, e.g., bootparams struct definitions, memory allocation/alignment requirements for the placement of the initrd etc etc. In the ARM case, it also requires the bootloader to modify the hardware description provided by the firmware, as it is passed in the same file. On systems where the initrd is measured after loading, it creates a time window where the initrd contents might be manipulated in memory before handing over to the kernel. Address these concerns by adding support for loading the initrd into memory by invoking the EFI LoadFile2 protocol installed on a vendor GUIDed device path that specifically designates a Linux initrd. This addresses the above concerns, by putting the EFI stub in charge of placement in memory and of passing the base and size to the kernel proper (via whatever means it desires) while still leaving it up to the firmware or bootloader to obtain the file contents, potentially from other file systems than the one the kernel itself was loaded from. On platforms that implement measured boot, it permits the firmware to take the measurement right before the kernel actually consumes the contents. Acked-by: Laszlo Ersek Tested-by: Ilias Apalodimas Acked-by: Ilias Apalodimas Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm-stub.c | 15 +++- .../firmware/efi/libstub/efi-stub-helper.c | 86 +++++++++++++++++++ drivers/firmware/efi/libstub/efistub.h | 4 + drivers/firmware/efi/libstub/x86-stub.c | 23 +++++ include/linux/efi.h | 1 + 5 files changed, 126 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index a11257a90dd0..11c673a7e95b 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -165,6 +165,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) enum efi_secureboot_mode secure_boot; struct screen_info *si; efi_properties_table_t *prop_tbl; + unsigned long max_addr; sys_table = sys_table_arg; @@ -267,10 +268,18 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!fdt_addr) pr_efi("Generating empty DTB\n"); - status = efi_load_initrd(image, &initrd_addr, &initrd_size, ULONG_MAX, - efi_get_max_initrd_addr(dram_base, image_addr)); + max_addr = efi_get_max_initrd_addr(dram_base, image_addr); + status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr); + if (status == EFI_SUCCESS) { + pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); + } else if (status == EFI_NOT_FOUND) { + status = efi_load_initrd(image, &initrd_addr, &initrd_size, + ULONG_MAX, max_addr); + if (status == EFI_SUCCESS) + pr_efi("Loaded initrd from command line option\n"); + } if (status != EFI_SUCCESS) - pr_efi_err("Failed initrd from command line!\n"); + pr_efi_err("Failed to load initrd!\n"); efi_random_get_seed(); diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 49008ac88b63..6017b968cef7 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -299,3 +299,89 @@ void efi_char16_printk(efi_char16_t *str) efi_call_proto(efi_table_attr(efi_system_table(), con_out), output_string, str); } + +/* + * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way + * for the firmware or bootloader to expose the initrd data directly to the stub + * via the trivial LoadFile2 protocol, which is defined in the UEFI spec, and is + * very easy to implement. It is a simple Linux initrd specific conduit between + * kernel and firmware, allowing us to put the EFI stub (being part of the + * kernel) in charge of where and when to load the initrd, while leaving it up + * to the firmware to decide whether it needs to expose its filesystem hierarchy + * via EFI protocols. + */ +static const struct { + struct efi_vendor_dev_path vendor; + struct efi_generic_dev_path end; +} __packed initrd_dev_path = { + { + { + EFI_DEV_MEDIA, + EFI_DEV_MEDIA_VENDOR, + sizeof(struct efi_vendor_dev_path), + }, + LINUX_EFI_INITRD_MEDIA_GUID + }, { + EFI_DEV_END_PATH, + EFI_DEV_END_ENTIRE, + sizeof(struct efi_generic_dev_path) + } +}; + +/** + * efi_load_initrd_dev_path - load the initrd from the Linux initrd device path + * @load_addr: pointer to store the address where the initrd was loaded + * @load_size: pointer to store the size of the loaded initrd + * @max: upper limit for the initrd memory allocation + * @return: %EFI_SUCCESS if the initrd was loaded successfully, in which + * case @load_addr and @load_size are assigned accordingly + * %EFI_NOT_FOUND if no LoadFile2 protocol exists on the initrd + * device path + * %EFI_INVALID_PARAMETER if load_addr == NULL or load_size == NULL + * %EFI_OUT_OF_RESOURCES if memory allocation failed + * %EFI_LOAD_ERROR in all other cases + */ +efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, + unsigned long *load_size, + unsigned long max) +{ + efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; + efi_device_path_protocol_t *dp; + efi_load_file2_protocol_t *lf2; + unsigned long initrd_addr; + unsigned long initrd_size; + efi_handle_t handle; + efi_status_t status; + + if (!load_addr || !load_size) + return EFI_INVALID_PARAMETER; + + dp = (efi_device_path_protocol_t *)&initrd_dev_path; + status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle); + if (status != EFI_SUCCESS) + return status; + + status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, + (void **)&lf2); + if (status != EFI_SUCCESS) + return status; + + status = efi_call_proto(lf2, load_file, dp, false, &initrd_size, NULL); + if (status != EFI_BUFFER_TOO_SMALL) + return EFI_LOAD_ERROR; + + status = efi_allocate_pages(initrd_size, &initrd_addr, max); + if (status != EFI_SUCCESS) + return status; + + status = efi_call_proto(lf2, load_file, dp, false, &initrd_size, + (void *)initrd_addr); + if (status != EFI_SUCCESS) { + efi_free(initrd_size, initrd_addr); + return EFI_LOAD_ERROR; + } + + *load_addr = initrd_addr; + *load_size = initrd_size; + return EFI_SUCCESS; +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 34fe3fad042f..b58cb2c4474e 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -640,4 +640,8 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image, unsigned long soft_limit, unsigned long hard_limit); +efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, + unsigned long *load_size, + unsigned long max); + #endif diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 681b620d8d40..16bf4ed21f1f 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -699,9 +699,14 @@ struct boot_params *efi_main(efi_handle_t handle, { unsigned long bzimage_addr = (unsigned long)startup_32; struct setup_header *hdr = &boot_params->hdr; + unsigned long max_addr = hdr->initrd_addr_max; + unsigned long initrd_addr, initrd_size; efi_status_t status; unsigned long cmdline_paddr; + if (hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) + max_addr = ULONG_MAX; + sys_table = sys_table_arg; /* Check if we were booted by the EFI firmware */ @@ -734,6 +739,24 @@ struct boot_params *efi_main(efi_handle_t handle, ((u64)boot_params->ext_cmd_line_ptr << 32)); efi_parse_options((char *)cmdline_paddr); + /* + * At this point, an initrd may already have been loaded, either by + * the bootloader and passed via bootparams, or loaded from a initrd= + * command line option by efi_pe_entry() above. In either case, we + * permit an initrd loaded from the LINUX_EFI_INITRD_MEDIA_GUID device + * path to supersede it. + */ + status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr); + if (status == EFI_SUCCESS) { + hdr->ramdisk_image = (u32)initrd_addr; + hdr->ramdisk_size = (u32)initrd_size; + boot_params->ext_ramdisk_image = (u64)initrd_addr >> 32; + boot_params->ext_ramdisk_size = (u64)initrd_size >> 32; + } else if (status != EFI_NOT_FOUND) { + efi_printk("efi_load_initrd_dev_path() failed!\n"); + goto fail; + } + /* * If the boot loader gave us a value for secure_boot then we use that, * otherwise we ask the BIOS. diff --git a/include/linux/efi.h b/include/linux/efi.h index 0976e57b4caa..1bf482daa22d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -353,6 +353,7 @@ void efi_native_runtime_setup(void); #define LINUX_EFI_TPM_EVENT_LOG_GUID EFI_GUID(0xb7799cb0, 0xeca2, 0x4943, 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa) #define LINUX_EFI_TPM_FINAL_LOG_GUID EFI_GUID(0x1e2ed096, 0x30e2, 0x4254, 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25) #define LINUX_EFI_MEMRESERVE_TABLE_GUID EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5, 0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2) +#define LINUX_EFI_INITRD_MEDIA_GUID EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) /* OEM GUIDs */ #define DELLEMC_EFI_RCI2_TABLE_GUID EFI_GUID(0x2d9f28a2, 0xa886, 0x456a, 0x97, 0xa8, 0xf1, 0x1e, 0xf2, 0x4f, 0xf4, 0x55) From 79d3219d4e56b3c47fc5184aa962dac886a73729 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 4 Feb 2020 22:01:22 +0000 Subject: [PATCH 0507/2231] efi/libstub: Take noinitrd cmdline argument into account for devpath initrd One of the advantages of using what basically amounts to a callback interface into the bootloader for loading the initrd is that it provides a natural place for the bootloader or firmware to measure the initrd contents while they are being passed to the kernel. Unfortunately, this is not a guarantee that the initrd will in fact be loaded and its /init invoked by the kernel, since the command line may contain the 'noinitrd' option, in which case the initrd is ignored, but this will not be reflected in the PCR that covers the initrd measurement. This could be addressed by measuring the command line as well, and including that PCR in the attestation policy, but this locks down the command line completely, which may be too restrictive. So let's take the noinitrd argument into account in the stub, too. This forces any PCR that covers the initrd to assume a different value when noinitrd is passed, allowing an attestation policy to disregard the command line if there is no need to take its measurement into account for other reasons. As Peter points out, this would still require the agent that takes the measurements to measure a separator event into the PCR in question at ExitBootServices() time, to prevent replay attacks using the known measurement from the TPM log. Cc: Peter Jones Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm-stub.c | 27 +++++----- .../firmware/efi/libstub/efi-stub-helper.c | 7 +++ drivers/firmware/efi/libstub/efistub.h | 1 + drivers/firmware/efi/libstub/x86-stub.c | 52 +++++++++++-------- 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 11c673a7e95b..13559c7e6643 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -153,7 +153,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) unsigned long image_size = 0; unsigned long dram_base; /* addr/point and size pairs for memory management*/ - unsigned long initrd_addr; + unsigned long initrd_addr = 0; unsigned long initrd_size = 0; unsigned long fdt_addr = 0; /* Original DTB */ unsigned long fdt_size = 0; @@ -268,18 +268,21 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!fdt_addr) pr_efi("Generating empty DTB\n"); - max_addr = efi_get_max_initrd_addr(dram_base, image_addr); - status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr); - if (status == EFI_SUCCESS) { - pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); - } else if (status == EFI_NOT_FOUND) { - status = efi_load_initrd(image, &initrd_addr, &initrd_size, - ULONG_MAX, max_addr); - if (status == EFI_SUCCESS) - pr_efi("Loaded initrd from command line option\n"); + if (!noinitrd()) { + max_addr = efi_get_max_initrd_addr(dram_base, image_addr); + status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, + max_addr); + if (status == EFI_SUCCESS) { + pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); + } else if (status == EFI_NOT_FOUND) { + status = efi_load_initrd(image, &initrd_addr, &initrd_size, + ULONG_MAX, max_addr); + if (status == EFI_SUCCESS) + pr_efi("Loaded initrd from command line option\n"); + } + if (status != EFI_SUCCESS) + pr_efi_err("Failed to load initrd!\n"); } - if (status != EFI_SUCCESS) - pr_efi_err("Failed to load initrd!\n"); efi_random_get_seed(); diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 6017b968cef7..b1da58141a4d 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -14,6 +14,7 @@ static bool __efistub_global efi_nochunk; static bool __efistub_global efi_nokaslr; +static bool __efistub_global efi_noinitrd; static bool __efistub_global efi_quiet; static bool __efistub_global efi_novamap; static bool __efistub_global efi_nosoftreserve; @@ -28,6 +29,10 @@ bool __pure nokaslr(void) { return efi_nokaslr; } +bool __pure noinitrd(void) +{ + return efi_noinitrd; +} bool __pure is_quiet(void) { return efi_quiet; @@ -87,6 +92,8 @@ efi_status_t efi_parse_options(char const *cmdline) efi_nokaslr = true; } else if (!strcmp(param, "quiet")) { efi_quiet = true; + } else if (!strcmp(param, "noinitrd")) { + efi_noinitrd = true; } else if (!strcmp(param, "efi") && val) { efi_nochunk = parse_option_str(val, "nochunk"); efi_novamap = parse_option_str(val, "novamap"); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index b58cb2c4474e..2e5e79edb4d7 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -33,6 +33,7 @@ extern bool __pure nochunk(void); extern bool __pure nokaslr(void); +extern bool __pure noinitrd(void); extern bool __pure is_quiet(void); extern bool __pure novamap(void); diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 16bf4ed21f1f..7d4866471f86 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -421,15 +421,18 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, if (status != EFI_SUCCESS) goto fail2; - status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size, - hdr->initrd_addr_max, - above4g ? ULONG_MAX : hdr->initrd_addr_max); - if (status != EFI_SUCCESS) - goto fail2; - hdr->ramdisk_image = ramdisk_addr & 0xffffffff; - hdr->ramdisk_size = ramdisk_size & 0xffffffff; - boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32; - boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32; + if (!noinitrd()) { + status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size, + hdr->initrd_addr_max, + above4g ? ULONG_MAX + : hdr->initrd_addr_max); + if (status != EFI_SUCCESS) + goto fail2; + hdr->ramdisk_image = ramdisk_addr & 0xffffffff; + hdr->ramdisk_size = ramdisk_size & 0xffffffff; + boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32; + boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32; + } efi_stub_entry(handle, sys_table, boot_params); /* not reached */ @@ -699,14 +702,9 @@ struct boot_params *efi_main(efi_handle_t handle, { unsigned long bzimage_addr = (unsigned long)startup_32; struct setup_header *hdr = &boot_params->hdr; - unsigned long max_addr = hdr->initrd_addr_max; - unsigned long initrd_addr, initrd_size; efi_status_t status; unsigned long cmdline_paddr; - if (hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) - max_addr = ULONG_MAX; - sys_table = sys_table_arg; /* Check if we were booted by the EFI firmware */ @@ -746,15 +744,23 @@ struct boot_params *efi_main(efi_handle_t handle, * permit an initrd loaded from the LINUX_EFI_INITRD_MEDIA_GUID device * path to supersede it. */ - status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr); - if (status == EFI_SUCCESS) { - hdr->ramdisk_image = (u32)initrd_addr; - hdr->ramdisk_size = (u32)initrd_size; - boot_params->ext_ramdisk_image = (u64)initrd_addr >> 32; - boot_params->ext_ramdisk_size = (u64)initrd_size >> 32; - } else if (status != EFI_NOT_FOUND) { - efi_printk("efi_load_initrd_dev_path() failed!\n"); - goto fail; + if (!noinitrd()) { + unsigned long addr, size; + unsigned long max_addr = hdr->initrd_addr_max; + + if (hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) + max_addr = ULONG_MAX; + + status = efi_load_initrd_dev_path(&addr, &size, max_addr); + if (status == EFI_SUCCESS) { + hdr->ramdisk_image = (u32)addr; + hdr->ramdisk_size = (u32)size; + boot_params->ext_ramdisk_image = (u64)addr >> 32; + boot_params->ext_ramdisk_size = (u64)size >> 32; + } else if (status != EFI_NOT_FOUND) { + efi_printk("efi_load_initrd_dev_path() failed!\n"); + goto fail; + } } /* From 50d53c58dd77d3b0b6a5afe391eaac3722fc3153 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 19 Jan 2020 15:29:21 +0100 Subject: [PATCH 0508/2231] efi: Drop handling of 'boot_info' configuration table Some plumbing exists to handle a UEFI configuration table of type BOOT_INFO but since we never match it to a GUID anywhere, we never actually register such a table, or access it, for that matter. So simply drop all mentions of it. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 1 - drivers/firmware/efi/efi.c | 3 --- include/linux/efi.h | 1 - 3 files changed, 5 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 52067ed7fd59..4970229fd822 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -70,7 +70,6 @@ static const unsigned long * const efi_tables[] = { &efi.acpi20, &efi.smbios, &efi.smbios3, - &efi.boot_info, &efi.hcdp, &efi.uga, #ifdef CONFIG_X86_UV diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 621220ab3d0e..5464e3849ee4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -40,7 +40,6 @@ struct efi __read_mostly efi = { .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR, - .boot_info = EFI_INVALID_TABLE_ADDR, .hcdp = EFI_INVALID_TABLE_ADDR, .uga = EFI_INVALID_TABLE_ADDR, .fw_vendor = EFI_INVALID_TABLE_ADDR, @@ -139,8 +138,6 @@ static ssize_t systab_show(struct kobject *kobj, str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); if (efi.hcdp != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); - if (efi.boot_info != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info); if (efi.uga != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "UGA=0x%lx\n", efi.uga); diff --git a/include/linux/efi.h b/include/linux/efi.h index 1bf482daa22d..c517d3b7986b 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -536,7 +536,6 @@ extern struct efi { unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long boot_info; /* boot info table */ unsigned long hcdp; /* HCDP table */ unsigned long uga; /* UGA table */ unsigned long fw_vendor; /* fw_vendor */ From 120540f230d5d2d32846adc0156b58961c8c59d1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 19 Jan 2020 15:43:53 +0100 Subject: [PATCH 0509/2231] efi/ia64: Move HCDP and MPS table handling into IA64 arch code The HCDP and MPS tables are Itanium specific EFI config tables, so move their handling to ia64 arch code. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 13 +++++++++++++ arch/x86/platform/efi/efi.c | 2 -- drivers/firmware/efi/efi.c | 14 ++++++-------- drivers/firmware/pcdp.c | 8 +++++--- include/linux/efi.h | 2 -- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 0a34dcc435c6..312308967a9d 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -45,11 +45,15 @@ #define EFI_DEBUG 0 +static unsigned long mps_phys = EFI_INVALID_TABLE_ADDR; static __initdata unsigned long palo_phys; +unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR; unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR; static __initdata efi_config_table_type_t arch_tables[] = { + {HCDP_TABLE_GUID, "HCDP", &hcdp_phys}, + {MPS_TABLE_GUID, "MPS", &mps_phys}, {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys}, {SAL_SYSTEM_TABLE_GUID, "SALsystab", &sal_systab_phys}, {NULL_GUID, NULL, 0}, @@ -1351,3 +1355,12 @@ vmcore_find_descriptor_size (unsigned long address) return ret; } #endif + +char *efi_systab_show_arch(char *str) +{ + if (mps_phys != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "MPS=0x%lx\n", mps_phys); + if (hcdp_phys != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "HCDP=0x%lx\n", hcdp_phys); + return str; +} diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 4970229fd822..61ebaae62894 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -65,12 +65,10 @@ static efi_config_table_type_t arch_tables[] __initdata = { }; static const unsigned long * const efi_tables[] = { - &efi.mps, &efi.acpi, &efi.acpi20, &efi.smbios, &efi.smbios3, - &efi.hcdp, &efi.uga, #ifdef CONFIG_X86_UV &uv_systab_phys, diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 5464e3849ee4..8129a52f8ef5 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -35,12 +35,10 @@ #include struct efi __read_mostly efi = { - .mps = EFI_INVALID_TABLE_ADDR, .acpi = EFI_INVALID_TABLE_ADDR, .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR, - .hcdp = EFI_INVALID_TABLE_ADDR, .uga = EFI_INVALID_TABLE_ADDR, .fw_vendor = EFI_INVALID_TABLE_ADDR, .runtime = EFI_INVALID_TABLE_ADDR, @@ -121,8 +119,6 @@ static ssize_t systab_show(struct kobject *kobj, if (!kobj || !buf) return -EINVAL; - if (efi.mps != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "MPS=0x%lx\n", efi.mps); if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); if (efi.acpi != EFI_INVALID_TABLE_ADDR) @@ -136,11 +132,15 @@ static ssize_t systab_show(struct kobject *kobj, str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); if (efi.smbios != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); - if (efi.hcdp != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); if (efi.uga != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "UGA=0x%lx\n", efi.uga); + if (IS_ENABLED(CONFIG_IA64)) { + extern char *efi_systab_show_arch(char *str); + + str = efi_systab_show_arch(str); + } + return str - buf; } @@ -467,8 +467,6 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size) static __initdata efi_config_table_type_t common_tables[] = { {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, - {HCDP_TABLE_GUID, "HCDP", &efi.hcdp}, - {MPS_TABLE_GUID, "MPS", &efi.mps}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c index 4adeb7a2bdf5..715a45442d1c 100644 --- a/drivers/firmware/pcdp.c +++ b/drivers/firmware/pcdp.c @@ -80,6 +80,8 @@ setup_vga_console(struct pcdp_device *dev) #endif } +extern unsigned long hcdp_phys; + int __init efi_setup_pcdp_console(char *cmdline) { @@ -89,11 +91,11 @@ efi_setup_pcdp_console(char *cmdline) int i, serial = 0; int rc = -ENODEV; - if (efi.hcdp == EFI_INVALID_TABLE_ADDR) + if (hcdp_phys == EFI_INVALID_TABLE_ADDR) return -ENODEV; - pcdp = early_memremap(efi.hcdp, 4096); - printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, efi.hcdp); + pcdp = early_memremap(hcdp_phys, 4096); + printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, hcdp_phys); if (strstr(cmdline, "console=hcdp")) { if (pcdp->rev < 3) diff --git a/include/linux/efi.h b/include/linux/efi.h index c517d3b7986b..45443932104f 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -531,12 +531,10 @@ typedef struct { extern struct efi { efi_system_table_t *systab; /* EFI system table */ unsigned int runtime_version; /* Runtime services version */ - unsigned long mps; /* MPS table */ unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long hcdp; /* HCDP table */ unsigned long uga; /* UGA table */ unsigned long fw_vendor; /* fw_vendor */ unsigned long runtime; /* runtime table */ From fd506e0cf9fd4306aa0eb57cbff5f00514da8179 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 19 Jan 2020 16:17:59 +0100 Subject: [PATCH 0510/2231] efi: Move UGA and PROP table handling to x86 code The UGA table is x86 specific (its handling was introduced when the EFI support code was modified to accommodate IA32), so there is no need to handle it in generic code. The EFI properties table is not strictly x86 specific, but it was deprecated almost immediately after having been introduced, due to implementation difficulties. Only x86 takes it into account today, and this is not going to change, so make this table x86 only as well. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 32 ++++++++++++++++++++++++++++++-- drivers/firmware/efi/efi.c | 25 +------------------------ include/linux/efi.h | 2 -- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 61ebaae62894..421f082535c5 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -57,7 +57,12 @@ static efi_system_table_t efi_systab __initdata; static u64 efi_systab_phys __initdata; +static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; +static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; + static efi_config_table_type_t arch_tables[] __initdata = { + {EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys}, + {UGA_IO_PROTOCOL_GUID, "UGA", &uga_phys}, #ifdef CONFIG_X86_UV {UV_SYSTEM_TABLE_GUID, "UVsystab", &uv_systab_phys}, #endif @@ -69,7 +74,7 @@ static const unsigned long * const efi_tables[] = { &efi.acpi20, &efi.smbios, &efi.smbios3, - &efi.uga, + &uga_phys, #ifdef CONFIG_X86_UV &uv_systab_phys, #endif @@ -77,7 +82,7 @@ static const unsigned long * const efi_tables[] = { &efi.runtime, &efi.config_table, &efi.esrt, - &efi.properties_table, + &prop_phys, &efi.mem_attr_table, #ifdef CONFIG_EFI_RCI2_TABLE &rci2_table_phys, @@ -490,6 +495,22 @@ void __init efi_init(void) return; } + /* Parse the EFI Properties table if it exists */ + if (prop_phys != EFI_INVALID_TABLE_ADDR) { + efi_properties_table_t *tbl; + + tbl = early_memremap_ro(prop_phys, sizeof(*tbl)); + if (tbl == NULL) { + pr_err("Could not map Properties table!\n"); + } else { + if (tbl->memory_protection_attribute & + EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) + set_bit(EFI_NX_PE_DATA, &efi.flags); + + early_memunmap(tbl, sizeof(*tbl)); + } + } + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); efi_clean_memmap(); @@ -993,3 +1014,10 @@ bool efi_is_table_address(unsigned long phys_addr) return false; } + +char *efi_systab_show_arch(char *str) +{ + if (uga_phys != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "UGA=0x%lx\n", uga_phys); + return str; +} diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 8129a52f8ef5..68527fbbe01c 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -39,12 +39,10 @@ struct efi __read_mostly efi = { .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR, - .uga = EFI_INVALID_TABLE_ADDR, .fw_vendor = EFI_INVALID_TABLE_ADDR, .runtime = EFI_INVALID_TABLE_ADDR, .config_table = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR, - .properties_table = EFI_INVALID_TABLE_ADDR, .mem_attr_table = EFI_INVALID_TABLE_ADDR, .rng_seed = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, @@ -132,10 +130,8 @@ static ssize_t systab_show(struct kobject *kobj, str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); if (efi.smbios != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); - if (efi.uga != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "UGA=0x%lx\n", efi.uga); - if (IS_ENABLED(CONFIG_IA64)) { + if (IS_ENABLED(CONFIG_IA64) || IS_ENABLED(CONFIG_X86)) { extern char *efi_systab_show_arch(char *str); str = efi_systab_show_arch(str); @@ -469,9 +465,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, - {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, - {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table}, {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, @@ -570,23 +564,6 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, efi_tpm_eventlog_init(); - /* Parse the EFI Properties table if it exists */ - if (efi.properties_table != EFI_INVALID_TABLE_ADDR) { - efi_properties_table_t *tbl; - - tbl = early_memremap(efi.properties_table, sizeof(*tbl)); - if (tbl == NULL) { - pr_err("Could not map Properties table!\n"); - return -ENOMEM; - } - - if (tbl->memory_protection_attribute & - EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) - set_bit(EFI_NX_PE_DATA, &efi.flags); - - early_memunmap(tbl, sizeof(*tbl)); - } - if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) { unsigned long prsv = efi.mem_reserve; diff --git a/include/linux/efi.h b/include/linux/efi.h index 45443932104f..e091f2aff61d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -535,12 +535,10 @@ extern struct efi { unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long uga; /* UGA table */ unsigned long fw_vendor; /* fw_vendor */ unsigned long runtime; /* runtime table */ unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ - unsigned long properties_table; /* properties table */ unsigned long mem_attr_table; /* memory attributes table */ unsigned long rng_seed; /* UEFI firmware random seed */ unsigned long tpm_log; /* TPM2 Event Log table */ From 5d288dbd88606d8f215c7138b10649115d79cadd Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Jan 2020 14:58:15 +0100 Subject: [PATCH 0511/2231] efi: Make rng_seed table handling local to efi.c Move the rng_seed table address from struct efi into a static global variable in efi.c, which is the only place we ever refer to it anyway. This reduces the footprint of struct efi, which is a r/w data structure that is shared with the world. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 21 ++++++++++----------- include/linux/efi.h | 1 - 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 68527fbbe01c..bbb6246d08be 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -44,13 +44,14 @@ struct efi __read_mostly efi = { .config_table = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR, .mem_attr_table = EFI_INVALID_TABLE_ADDR, - .rng_seed = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, .mem_reserve = EFI_INVALID_TABLE_ADDR, }; EXPORT_SYMBOL(efi); +static unsigned long __ro_after_init rng_seed = EFI_INVALID_TABLE_ADDR; + struct mm_struct efi_mm = { .mm_rb = RB_ROOT, .mm_users = ATOMIC_INIT(2), @@ -467,7 +468,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, - {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed}, + {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve}, @@ -535,11 +536,11 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, pr_cont("\n"); set_bit(EFI_CONFIG_TABLES, &efi.flags); - if (efi.rng_seed != EFI_INVALID_TABLE_ADDR) { + if (rng_seed != EFI_INVALID_TABLE_ADDR) { struct linux_efi_random_seed *seed; u32 size = 0; - seed = early_memremap(efi.rng_seed, sizeof(*seed)); + seed = early_memremap(rng_seed, sizeof(*seed)); if (seed != NULL) { size = seed->size; early_memunmap(seed, sizeof(*seed)); @@ -547,8 +548,7 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, pr_err("Could not map UEFI random seed!\n"); } if (size > 0) { - seed = early_memremap(efi.rng_seed, - sizeof(*seed) + size); + seed = early_memremap(rng_seed, sizeof(*seed) + size); if (seed != NULL) { pr_notice("seeding entropy pool\n"); add_bootloader_randomness(seed->bits, seed->size); @@ -1048,7 +1048,7 @@ static int update_efi_random_seed(struct notifier_block *nb, if (!kexec_in_progress) return NOTIFY_DONE; - seed = memremap(efi.rng_seed, sizeof(*seed), MEMREMAP_WB); + seed = memremap(rng_seed, sizeof(*seed), MEMREMAP_WB); if (seed != NULL) { size = min(seed->size, EFI_RANDOM_SEED_SIZE); memunmap(seed); @@ -1056,8 +1056,7 @@ static int update_efi_random_seed(struct notifier_block *nb, pr_err("Could not map UEFI random seed!\n"); } if (size > 0) { - seed = memremap(efi.rng_seed, sizeof(*seed) + size, - MEMREMAP_WB); + seed = memremap(rng_seed, sizeof(*seed) + size, MEMREMAP_WB); if (seed != NULL) { seed->size = size; get_random_bytes(seed->bits, seed->size); @@ -1073,9 +1072,9 @@ static struct notifier_block efi_random_seed_nb = { .notifier_call = update_efi_random_seed, }; -static int register_update_efi_random_seed(void) +static int __init register_update_efi_random_seed(void) { - if (efi.rng_seed == EFI_INVALID_TABLE_ADDR) + if (rng_seed == EFI_INVALID_TABLE_ADDR) return 0; return register_reboot_notifier(&efi_random_seed_nb); } diff --git a/include/linux/efi.h b/include/linux/efi.h index e091f2aff61d..36380542e054 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -540,7 +540,6 @@ extern struct efi { unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ unsigned long mem_attr_table; /* memory attributes table */ - unsigned long rng_seed; /* UEFI firmware random seed */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ unsigned long mem_reserve; /* Linux EFI memreserve table */ From a17e809ea573e69474064ba2bbff06d212861e19 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Jan 2020 15:05:12 +0100 Subject: [PATCH 0512/2231] efi: Move mem_attr_table out of struct efi The memory attributes table is only used at init time by the core EFI code, so there is no need to carry its address in struct efi that is shared with the world. So move it out, and make it __ro_after_init as well, considering that the value is set during early boot. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 2 +- drivers/firmware/efi/efi.c | 3 +-- drivers/firmware/efi/memattr.c | 13 +++++++------ include/linux/efi.h | 3 ++- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 421f082535c5..22dc3678cdba 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -83,7 +83,7 @@ static const unsigned long * const efi_tables[] = { &efi.config_table, &efi.esrt, &prop_phys, - &efi.mem_attr_table, + &efi_mem_attr_table, #ifdef CONFIG_EFI_RCI2_TABLE &rci2_table_phys, #endif diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index bbb6246d08be..1fc4e174f11d 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -43,7 +43,6 @@ struct efi __read_mostly efi = { .runtime = EFI_INVALID_TABLE_ADDR, .config_table = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR, - .mem_attr_table = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, .mem_reserve = EFI_INVALID_TABLE_ADDR, @@ -467,7 +466,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, - {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, + {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi_mem_attr_table}, {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c index 58452fde92cc..5737cb0fcd44 100644 --- a/drivers/firmware/efi/memattr.c +++ b/drivers/firmware/efi/memattr.c @@ -13,6 +13,7 @@ #include static int __initdata tbl_size; +unsigned long __ro_after_init efi_mem_attr_table = EFI_INVALID_TABLE_ADDR; /* * Reserve the memory associated with the Memory Attributes configuration @@ -22,13 +23,13 @@ int __init efi_memattr_init(void) { efi_memory_attributes_table_t *tbl; - if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR) + if (efi_mem_attr_table == EFI_INVALID_TABLE_ADDR) return 0; - tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl)); + tbl = early_memremap(efi_mem_attr_table, sizeof(*tbl)); if (!tbl) { pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n", - efi.mem_attr_table); + efi_mem_attr_table); return -ENOMEM; } @@ -39,7 +40,7 @@ int __init efi_memattr_init(void) } tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size; - memblock_reserve(efi.mem_attr_table, tbl_size); + memblock_reserve(efi_mem_attr_table, tbl_size); set_bit(EFI_MEM_ATTR, &efi.flags); unmap: @@ -147,10 +148,10 @@ int __init efi_memattr_apply_permissions(struct mm_struct *mm, if (WARN_ON(!efi_enabled(EFI_MEMMAP))) return 0; - tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB); + tbl = memremap(efi_mem_attr_table, tbl_size, MEMREMAP_WB); if (!tbl) { pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n", - efi.mem_attr_table); + efi_mem_attr_table); return -ENOMEM; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 36380542e054..b093fce1cf59 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -539,7 +539,6 @@ extern struct efi { unsigned long runtime; /* runtime table */ unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ - unsigned long mem_attr_table; /* memory attributes table */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ unsigned long mem_reserve; /* Linux EFI memreserve table */ @@ -641,6 +640,8 @@ extern void __init efi_fake_memmap(void); static inline void efi_fake_memmap(void) { } #endif +extern unsigned long efi_mem_attr_table; + /* * efi_memattr_perm_setter - arch specific callback function passed into * efi_memattr_apply_permissions() that updates the From b7846e6be235c4a19337a32168b27ed836a1504e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Jan 2020 15:06:54 +0100 Subject: [PATCH 0513/2231] efi: Make memreserve table handling local to efi.c There is no need for struct efi to carry the address of the memreserve table and share it with the world. So move it out and make it __initdata as well. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 12 ++++++------ include/linux/efi.h | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 1fc4e174f11d..41bb2c44cea4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -45,11 +45,11 @@ struct efi __read_mostly efi = { .esrt = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, - .mem_reserve = EFI_INVALID_TABLE_ADDR, }; EXPORT_SYMBOL(efi); static unsigned long __ro_after_init rng_seed = EFI_INVALID_TABLE_ADDR; +static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; struct mm_struct efi_mm = { .mm_rb = RB_ROOT, @@ -470,7 +470,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, - {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve}, + {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve}, #ifdef CONFIG_EFI_RCI2_TABLE {DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys}, #endif @@ -563,8 +563,8 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, efi_tpm_eventlog_init(); - if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) { - unsigned long prsv = efi.mem_reserve; + if (mem_reserve != EFI_INVALID_TABLE_ADDR) { + unsigned long prsv = mem_reserve; while (prsv) { struct linux_efi_memreserve *rsv; @@ -939,10 +939,10 @@ static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init; static int __init efi_memreserve_map_root(void) { - if (efi.mem_reserve == EFI_INVALID_TABLE_ADDR) + if (mem_reserve == EFI_INVALID_TABLE_ADDR) return -ENODEV; - efi_memreserve_root = memremap(efi.mem_reserve, + efi_memreserve_root = memremap(mem_reserve, sizeof(*efi_memreserve_root), MEMREMAP_WB); if (WARN_ON_ONCE(!efi_memreserve_root)) diff --git a/include/linux/efi.h b/include/linux/efi.h index b093fce1cf59..a5e210abe4ca 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -541,7 +541,6 @@ extern struct efi { unsigned long esrt; /* ESRT table */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ - unsigned long mem_reserve; /* Linux EFI memreserve table */ efi_get_time_t *get_time; efi_set_time_t *set_time; efi_get_wakeup_time_t *get_wakeup_time; From 14fb4209094355928d5a742e35afabdf7b404c17 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 10:49:11 +0100 Subject: [PATCH 0514/2231] efi: Merge EFI system table revision and vendor checks We have three different versions of the code that checks the EFI system table revision and copies the firmware vendor string, and they are mostly equivalent, with the exception of the use of early_memremap_ro vs. __va() and the lowest major revision to warn about. Let's move this into common code and factor out the commonalities. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 23 ++----------- arch/x86/platform/efi/efi.c | 46 ++++++------------------- drivers/firmware/efi/arm-init.c | 32 +++-------------- drivers/firmware/efi/efi.c | 61 +++++++++++++++++++++++++++++++++ include/linux/efi.h | 4 +++ 5 files changed, 83 insertions(+), 83 deletions(-) diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 312308967a9d..292fe354158d 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -479,10 +479,8 @@ void __init efi_init (void) { void *efi_map_start, *efi_map_end; - efi_char16_t *c16; u64 efi_desc_size; - char *cp, vendor[100] = "unknown"; - int i; + char *cp; set_bit(EFI_BOOT, &efi.flags); set_bit(EFI_64BIT, &efi.flags); @@ -519,25 +517,10 @@ efi_init (void) */ if (efi.systab == NULL) panic("Whoa! Can't find EFI system table.\n"); - if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + if (efi_systab_check_header(&efi.systab->hdr, 1)) panic("Whoa! EFI system table signature incorrect\n"); - if ((efi.systab->hdr.revision >> 16) == 0) - printk(KERN_WARNING "Warning: EFI system table version " - "%d.%02d, expected 1.00 or greater\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff); - /* Show what we know for posterity */ - c16 = __va(efi.systab->fw_vendor); - if (c16) { - for (i = 0;i < (int) sizeof(vendor) - 1 && *c16; ++i) - vendor[i] = *c16++; - vendor[i] = '\0'; - } - - printk(KERN_INFO "EFI v%u.%.02u by %s:", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff, vendor); + efi_systab_report_header(&efi.systab->hdr, efi.systab->fw_vendor); palo_phys = EFI_INVALID_TABLE_ADDR; diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 22dc3678cdba..5bb53da48a4b 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -336,15 +336,23 @@ static int __init efi_systab_init(u64 phys) { int size = efi_enabled(EFI_64BIT) ? sizeof(efi_system_table_64_t) : sizeof(efi_system_table_32_t); + const efi_table_hdr_t *hdr; bool over4g = false; void *p; + int ret; - p = early_memremap_ro(phys, size); + hdr = p = early_memremap_ro(phys, size); if (p == NULL) { pr_err("Couldn't map the system table!\n"); return -ENOMEM; } + ret = efi_systab_check_header(hdr, 1); + if (ret) { + early_memunmap(p, size); + return ret; + } + if (efi_enabled(EFI_64BIT)) { const efi_system_table_64_t *systab64 = p; @@ -411,6 +419,7 @@ static int __init efi_systab_init(u64 phys) efi_systab.tables = systab32->tables; } + efi_systab_report_header(hdr, efi_systab.fw_vendor); early_memunmap(p, size); if (IS_ENABLED(CONFIG_X86_32) && over4g) { @@ -419,28 +428,11 @@ static int __init efi_systab_init(u64 phys) } efi.systab = &efi_systab; - - /* - * Verify the EFI Table - */ - if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - pr_err("System table signature incorrect!\n"); - return -EINVAL; - } - if ((efi.systab->hdr.revision >> 16) == 0) - pr_err("Warning: System table version %d.%02d, expected 1.00 or greater!\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff); - return 0; } void __init efi_init(void) { - efi_char16_t *c16; - char vendor[100] = "unknown"; - int i = 0; - if (IS_ENABLED(CONFIG_X86_32) && (boot_params.efi_info.efi_systab_hi || boot_params.efi_info.efi_memmap_hi)) { @@ -458,24 +450,6 @@ void __init efi_init(void) efi.fw_vendor = (unsigned long)efi.systab->fw_vendor; efi.runtime = (unsigned long)efi.systab->runtime; - /* - * Show what we know for posterity - */ - c16 = early_memremap_ro(efi.systab->fw_vendor, - sizeof(vendor) * sizeof(efi_char16_t)); - if (c16) { - for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i) - vendor[i] = c16[i]; - vendor[i] = '\0'; - early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t)); - } else { - pr_err("Could not map the firmware vendor!\n"); - } - - pr_info("EFI v%u.%.02u by %s\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff, vendor); - if (efi_reuse_config(efi.systab->tables, efi.systab->nr_tables)) return; diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index d99f5b0c8a09..a656bfcd7e27 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -85,11 +85,9 @@ static void __init init_screen_info(void) static int __init uefi_init(void) { - efi_char16_t *c16; void *config_tables; size_t table_size; - char vendor[100] = "unknown"; - int i, retval; + int retval; efi.systab = early_memremap_ro(efi_system_table, sizeof(efi_system_table_t)); @@ -102,34 +100,14 @@ static int __init uefi_init(void) if (IS_ENABLED(CONFIG_64BIT)) set_bit(EFI_64BIT, &efi.flags); - /* - * Verify the EFI Table - */ - if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - pr_err("System table signature incorrect\n"); - retval = -EINVAL; + retval = efi_systab_check_header(&efi.systab->hdr, 2); + if (retval) goto out; - } - if ((efi.systab->hdr.revision >> 16) < 2) - pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff); efi.runtime_version = efi.systab->hdr.revision; - /* Show what we know for posterity */ - c16 = early_memremap_ro(efi_to_phys(efi.systab->fw_vendor), - sizeof(vendor) * sizeof(efi_char16_t)); - if (c16) { - for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) - vendor[i] = c16[i]; - vendor[i] = '\0'; - early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t)); - } - - pr_info("EFI v%u.%.02u by %s\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff, vendor); + efi_systab_report_header(&efi.systab->hdr, + efi_to_phys(efi.systab->fw_vendor)); table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables; config_tables = early_memremap_ro(efi_to_phys(efi.systab->tables), diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 41bb2c44cea4..80fe0044f2e2 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -631,6 +631,67 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) return ret; } + +int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr, + int min_major_version) +{ + if (systab_hdr->signature != EFI_SYSTEM_TABLE_SIGNATURE) { + pr_err("System table signature incorrect!\n"); + return -EINVAL; + } + + if ((systab_hdr->revision >> 16) < min_major_version) + pr_err("Warning: System table version %d.%02d, expected %d.00 or greater!\n", + systab_hdr->revision >> 16, + systab_hdr->revision & 0xffff, + min_major_version); + + return 0; +} + +#ifndef CONFIG_IA64 +static const efi_char16_t *__init map_fw_vendor(unsigned long fw_vendor, + size_t size) +{ + const efi_char16_t *ret; + + ret = early_memremap_ro(fw_vendor, size); + if (!ret) + pr_err("Could not map the firmware vendor!\n"); + return ret; +} + +static void __init unmap_fw_vendor(const void *fw_vendor, size_t size) +{ + early_memunmap((void *)fw_vendor, size); +} +#else +#define map_fw_vendor(p, s) __va(p) +#define unmap_fw_vendor(v, s) +#endif + +void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, + unsigned long fw_vendor) +{ + char vendor[100] = "unknown"; + const efi_char16_t *c16; + size_t i; + + c16 = map_fw_vendor(fw_vendor, sizeof(vendor) * sizeof(efi_char16_t)); + if (c16) { + for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i) + vendor[i] = c16[i]; + vendor[i] = '\0'; + + unmap_fw_vendor(c16, sizeof(vendor) * sizeof(efi_char16_t)); + } + + pr_info("EFI v%u.%.02u by %s\n", + systab_hdr->revision >> 16, + systab_hdr->revision & 0xffff, + vendor); +} + #ifdef CONFIG_EFI_VARS_MODULE static int __init efi_load_efivars(void) { diff --git a/include/linux/efi.h b/include/linux/efi.h index a5e210abe4ca..287510e84dfb 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -616,6 +616,10 @@ static inline void efi_esrt_init(void) { } #endif extern int efi_config_parse_tables(void *config_tables, int count, int sz, efi_config_table_type_t *arch_tables); +extern int efi_systab_check_header(const efi_table_hdr_t *systab_hdr, + int min_major_version); +extern void efi_systab_report_header(const efi_table_hdr_t *systab_hdr, + unsigned long fw_vendor); extern u64 efi_get_iobase (void); extern int efi_mem_type(unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); From c0019f576818b6e867bcb1feece10d9a21a7bc9f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 15:45:39 +0100 Subject: [PATCH 0515/2231] efi/ia64: Use existing helpers to locate ESI table Instead of iterating over the EFI config table array manually, declare it as an arch table so it gets picked up by the existing config table handling code. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 6 ++++++ arch/ia64/kernel/esi.c | 21 ++++----------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 292fe354158d..74fad89ae209 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -45,13 +45,19 @@ #define EFI_DEBUG 0 +#define ESI_TABLE_GUID \ + EFI_GUID(0x43EA58DC, 0xCF28, 0x4b06, 0xB3, \ + 0x91, 0xB7, 0x50, 0x59, 0x34, 0x2B, 0xD4) + static unsigned long mps_phys = EFI_INVALID_TABLE_ADDR; static __initdata unsigned long palo_phys; +unsigned long __initdata esi_phys = EFI_INVALID_TABLE_ADDR; unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR; unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR; static __initdata efi_config_table_type_t arch_tables[] = { + {ESI_TABLE_GUID, "ESI", &esi_phys}, {HCDP_TABLE_GUID, "HCDP", &hcdp_phys}, {MPS_TABLE_GUID, "MPS", &mps_phys}, {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys}, diff --git a/arch/ia64/kernel/esi.c b/arch/ia64/kernel/esi.c index cb514126ef7f..4df57c93e0a8 100644 --- a/arch/ia64/kernel/esi.c +++ b/arch/ia64/kernel/esi.c @@ -19,10 +19,6 @@ MODULE_LICENSE("GPL"); #define MODULE_NAME "esi" -#define ESI_TABLE_GUID \ - EFI_GUID(0x43EA58DC, 0xCF28, 0x4b06, 0xB3, \ - 0x91, 0xB7, 0x50, 0x59, 0x34, 0x2B, 0xD4) - enum esi_systab_entry_type { ESI_DESC_ENTRY_POINT = 0 }; @@ -48,27 +44,18 @@ struct pdesc { static struct ia64_sal_systab *esi_systab; +extern unsigned long esi_phys; + static int __init esi_init (void) { - efi_config_table_t *config_tables; struct ia64_sal_systab *systab; - unsigned long esi = 0; char *p; int i; - config_tables = __va(efi.systab->tables); - - for (i = 0; i < (int) efi.systab->nr_tables; ++i) { - if (efi_guidcmp(config_tables[i].guid, ESI_TABLE_GUID) == 0) { - esi = config_tables[i].table; - break; - } - } - - if (!esi) + if (esi_phys == EFI_INVALID_TABLE_ADDR) return -ENODEV; - systab = __va(esi); + systab = __va(esi_phys); if (strncmp(systab->signature, "ESIT", 4) != 0) { printk(KERN_ERR "bad signature in ESI system table!"); From beaf1c745b46c567c1c63bb8d16d2a72aacf5dd1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 15:48:07 +0100 Subject: [PATCH 0516/2231] efi/ia64: Use local variable for EFI system table address The IA64 code never refers to the EFI system table except from inside the scope of efi_init(). So let's use a local variable instead of efi.systab, which will be going away soon. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 74fad89ae209..81bc5031a115 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -484,6 +484,7 @@ efi_map_pal_code (void) void __init efi_init (void) { + const efi_system_table_t *efi_systab; void *efi_map_start, *efi_map_end; u64 efi_desc_size; char *cp; @@ -516,17 +517,17 @@ efi_init (void) printk(KERN_INFO "Ignoring memory above %lluMB\n", max_addr >> 20); - efi.systab = __va(ia64_boot_param->efi_systab); + efi_systab = __va(ia64_boot_param->efi_systab); /* * Verify the EFI Table */ - if (efi.systab == NULL) + if (efi_systab == NULL) panic("Whoa! Can't find EFI system table.\n"); - if (efi_systab_check_header(&efi.systab->hdr, 1)) + if (efi_systab_check_header(&efi_systab->hdr, 1)) panic("Whoa! EFI system table signature incorrect\n"); - efi_systab_report_header(&efi.systab->hdr, efi.systab->fw_vendor); + efi_systab_report_header(&efi_systab->hdr, efi_systab->fw_vendor); palo_phys = EFI_INVALID_TABLE_ADDR; @@ -536,7 +537,7 @@ efi_init (void) if (palo_phys != EFI_INVALID_TABLE_ADDR) handle_palo(palo_phys); - runtime = __va(efi.systab->runtime); + runtime = __va(efi_systab->runtime); efi.get_time = phys_get_time; efi.set_time = phys_set_time; efi.get_wakeup_time = phys_get_wakeup_time; From 686312927b13fc30b23b0e0f9be097c292343048 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 14:58:09 +0100 Subject: [PATCH 0517/2231] efi/ia64: Switch to efi_config_parse_tables() IA64 calls efi_config_parse_tables() via efi_config_init(), which does an explicit memremap() of the tables, which is unnecessary on IA64. So let's call efi_config_parse_tables() directly, passing the __va() of the config table array. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 81bc5031a115..3b5cf551489c 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -531,7 +531,10 @@ efi_init (void) palo_phys = EFI_INVALID_TABLE_ADDR; - if (efi_config_init(arch_tables) != 0) + if (efi_config_parse_tables(__va(efi_systab->tables), + efi_systab->nr_tables, + sizeof(efi_config_table_t), + arch_tables) != 0) return; if (palo_phys != EFI_INVALID_TABLE_ADDR) From 3a0701dc7ff8ebe1031a9f64c99c638929cd2d70 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 17:17:27 +0100 Subject: [PATCH 0518/2231] efi: Make efi_config_init() x86 only The efi_config_init() routine is no longer shared with ia64 so let's move it into the x86 arch code before making further x86 specific changes to it. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 30 ++++++++++++++++++++++++++++++ drivers/firmware/efi/efi.c | 31 ------------------------------- include/linux/efi.h | 1 - 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 5bb53da48a4b..f1033f7f9e39 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -431,6 +431,36 @@ static int __init efi_systab_init(u64 phys) return 0; } +static int __init efi_config_init(efi_config_table_type_t *arch_tables) +{ + void *config_tables; + int sz, ret; + + if (efi.systab->nr_tables == 0) + return 0; + + if (efi_enabled(EFI_64BIT)) + sz = sizeof(efi_config_table_64_t); + else + sz = sizeof(efi_config_table_32_t); + + /* + * Let's see what config tables the firmware passed to us. + */ + config_tables = early_memremap(efi.systab->tables, + efi.systab->nr_tables * sz); + if (config_tables == NULL) { + pr_err("Could not map Configuration table!\n"); + return -ENOMEM; + } + + ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, + arch_tables); + + early_memunmap(config_tables, efi.systab->nr_tables * sz); + return ret; +} + void __init efi_init(void) { if (IS_ENABLED(CONFIG_X86_32) && diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 80fe0044f2e2..2bfd6c0806ce 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -601,37 +601,6 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, return 0; } -int __init efi_config_init(efi_config_table_type_t *arch_tables) -{ - void *config_tables; - int sz, ret; - - if (efi.systab->nr_tables == 0) - return 0; - - if (efi_enabled(EFI_64BIT)) - sz = sizeof(efi_config_table_64_t); - else - sz = sizeof(efi_config_table_32_t); - - /* - * Let's see what config tables the firmware passed to us. - */ - config_tables = early_memremap(efi.systab->tables, - efi.systab->nr_tables * sz); - if (config_tables == NULL) { - pr_err("Could not map Configuration table!\n"); - return -ENOMEM; - } - - ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, - arch_tables); - - early_memunmap(config_tables, efi.systab->nr_tables * sz); - return ret; -} - - int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr, int min_major_version) { diff --git a/include/linux/efi.h b/include/linux/efi.h index 287510e84dfb..d61c25fd5824 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -608,7 +608,6 @@ extern int __init efi_memmap_split_count(efi_memory_desc_t *md, extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, struct efi_mem_range *mem); -extern int efi_config_init(efi_config_table_type_t *arch_tables); #ifdef CONFIG_EFI_ESRT extern void __init efi_esrt_init(void); #else From 06c0bd93434c5b9b284773f90bb054aff591d5be Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Jan 2020 14:40:57 +0100 Subject: [PATCH 0519/2231] efi: Clean up config_parse_tables() config_parse_tables() is a jumble of pointer arithmetic, due to the fact that on x86, we may be dealing with firmware whose native word size differs from the kernel's. This is not a concern on other architectures, and doesn't quite justify the state of the code, so let's clean it up by adding a non-x86 code path, constifying statically allocated tables and replacing preprocessor conditionals with IS_ENABLED() checks. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 3 +-- arch/x86/platform/efi/efi.c | 6 ++--- drivers/firmware/efi/arm-init.c | 5 ++-- drivers/firmware/efi/efi.c | 45 ++++++++++++++++----------------- include/linux/efi.h | 5 ++-- 5 files changed, 31 insertions(+), 33 deletions(-) diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 3b5cf551489c..f69f3fe0532e 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -56,7 +56,7 @@ unsigned long __initdata esi_phys = EFI_INVALID_TABLE_ADDR; unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR; unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR; -static __initdata efi_config_table_type_t arch_tables[] = { +static const efi_config_table_type_t arch_tables[] __initconst = { {ESI_TABLE_GUID, "ESI", &esi_phys}, {HCDP_TABLE_GUID, "HCDP", &hcdp_phys}, {MPS_TABLE_GUID, "MPS", &mps_phys}, @@ -533,7 +533,6 @@ efi_init (void) if (efi_config_parse_tables(__va(efi_systab->tables), efi_systab->nr_tables, - sizeof(efi_config_table_t), arch_tables) != 0) return; diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index f1033f7f9e39..47367f4d82d0 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -60,7 +60,7 @@ static u64 efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; -static efi_config_table_type_t arch_tables[] __initdata = { +static const efi_config_table_type_t arch_tables[] __initconst = { {EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys}, {UGA_IO_PROTOCOL_GUID, "UGA", &uga_phys}, #ifdef CONFIG_X86_UV @@ -431,7 +431,7 @@ static int __init efi_systab_init(u64 phys) return 0; } -static int __init efi_config_init(efi_config_table_type_t *arch_tables) +static int __init efi_config_init(const efi_config_table_type_t *arch_tables) { void *config_tables; int sz, ret; @@ -454,7 +454,7 @@ static int __init efi_config_init(efi_config_table_type_t *arch_tables) return -ENOMEM; } - ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, + ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, arch_tables); early_memunmap(config_tables, efi.systab->nr_tables * sz); diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index a656bfcd7e27..d1f44c847841 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -55,7 +55,7 @@ static phys_addr_t efi_to_phys(unsigned long addr) static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR; -static __initdata efi_config_table_type_t arch_tables[] = { +static const efi_config_table_type_t arch_tables[] __initconst = { {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table}, {NULL_GUID, NULL, NULL} }; @@ -85,7 +85,7 @@ static void __init init_screen_info(void) static int __init uefi_init(void) { - void *config_tables; + efi_config_table_t *config_tables; size_t table_size; int retval; @@ -118,7 +118,6 @@ static int __init uefi_init(void) goto out; } retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, - sizeof(efi_config_table_t), arch_tables); if (!retval) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 2bfd6c0806ce..45de4c4a696b 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -460,7 +460,7 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size) efi_arch_mem_reserve(addr, size); } -static __initdata efi_config_table_type_t common_tables[] = { +static const efi_config_table_type_t common_tables[] __initconst = { {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, @@ -477,9 +477,9 @@ static __initdata efi_config_table_type_t common_tables[] = { {NULL_GUID, NULL, NULL}, }; -static __init int match_config_table(efi_guid_t *guid, +static __init int match_config_table(const efi_guid_t *guid, unsigned long table, - efi_config_table_type_t *table_types) + const efi_config_table_type_t *table_types) { int i; @@ -498,39 +498,38 @@ static __init int match_config_table(efi_guid_t *guid, return 0; } -int __init efi_config_parse_tables(void *config_tables, int count, int sz, - efi_config_table_type_t *arch_tables) +int __init efi_config_parse_tables(const efi_config_table_t *config_tables, + int count, + const efi_config_table_type_t *arch_tables) { - void *tablep; + const efi_config_table_64_t *tbl64 = (void *)config_tables; + const efi_config_table_32_t *tbl32 = (void *)config_tables; + const efi_guid_t *guid; + unsigned long table; int i; - tablep = config_tables; pr_info(""); for (i = 0; i < count; i++) { - efi_guid_t guid; - unsigned long table; + if (!IS_ENABLED(CONFIG_X86)) { + guid = &config_tables[i].guid; + table = (unsigned long)config_tables[i].table; + } else if (efi_enabled(EFI_64BIT)) { + guid = &tbl64[i].guid; + table = tbl64[i].table; - if (efi_enabled(EFI_64BIT)) { - u64 table64; - guid = ((efi_config_table_64_t *)tablep)->guid; - table64 = ((efi_config_table_64_t *)tablep)->table; - table = table64; -#ifndef CONFIG_64BIT - if (table64 >> 32) { + if (IS_ENABLED(CONFIG_X86_32) && + tbl64[i].table > U32_MAX) { pr_cont("\n"); pr_err("Table located above 4GB, disabling EFI.\n"); return -EINVAL; } -#endif } else { - guid = ((efi_config_table_32_t *)tablep)->guid; - table = ((efi_config_table_32_t *)tablep)->table; + guid = &tbl32[i].guid; + table = tbl32[i].table; } - if (!match_config_table(&guid, table, common_tables)) - match_config_table(&guid, table, arch_tables); - - tablep += sz; + if (!match_config_table(guid, table, common_tables)) + match_config_table(guid, table, arch_tables); } pr_cont("\n"); set_bit(EFI_CONFIG_TABLES, &efi.flags); diff --git a/include/linux/efi.h b/include/linux/efi.h index d61c25fd5824..99a7fcbe5e9b 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -613,8 +613,9 @@ extern void __init efi_esrt_init(void); #else static inline void efi_esrt_init(void) { } #endif -extern int efi_config_parse_tables(void *config_tables, int count, int sz, - efi_config_table_type_t *arch_tables); +extern int efi_config_parse_tables(const efi_config_table_t *config_tables, + int count, + const efi_config_table_type_t *arch_tables); extern int efi_systab_check_header(const efi_table_hdr_t *systab_hdr, int min_major_version); extern void efi_systab_report_header(const efi_table_hdr_t *systab_hdr, From 0a67361dcdaa29dca1e77ebac919c62e93a8b3bc Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 16:15:00 +0100 Subject: [PATCH 0520/2231] efi/x86: Remove runtime table address from kexec EFI setup data Since commit 33b85447fa61946b ("efi/x86: Drop two near identical versions of efi_runtime_init()"), we no longer map the EFI runtime services table before calling SetVirtualAddressMap(), which means we don't need the 1:1 mapped physical address of this table, and so there is no point in passing the address via EFI setup data on kexec boot. Note that the kexec tools will still look for this address in sysfs, so we still need to provide it. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 1 - arch/x86/kernel/kexec-bzimage64.c | 1 - arch/x86/platform/efi/efi.c | 4 +--- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index fcb21e3d13c5..ee867f01b2f6 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -178,7 +178,6 @@ extern void __init efi_uv1_memmap_phys_epilog(pgd_t *save_pgd); struct efi_setup_data { u64 fw_vendor; - u64 runtime; u64 tables; u64 smbios; u64 reserved[8]; diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index f293d872602a..f400678bd345 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -142,7 +142,6 @@ prepare_add_efi_setup_data(struct boot_params *params, struct efi_setup_data *esd = (void *)sd + sizeof(struct setup_data); esd->fw_vendor = efi.fw_vendor; - esd->runtime = efi.runtime; esd->tables = efi.config_table; esd->smbios = efi.smbios; diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 47367f4d82d0..7d932452a40f 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -376,6 +376,7 @@ static int __init efi_systab_init(u64 phys) systab64->con_out > U32_MAX || systab64->stderr_handle > U32_MAX || systab64->stderr > U32_MAX || + systab64->runtime > U32_MAX || systab64->boottime > U32_MAX; if (efi_setup) { @@ -388,17 +389,14 @@ static int __init efi_systab_init(u64 phys) } efi_systab.fw_vendor = (unsigned long)data->fw_vendor; - efi_systab.runtime = (void *)(unsigned long)data->runtime; efi_systab.tables = (unsigned long)data->tables; over4g |= data->fw_vendor > U32_MAX || - data->runtime > U32_MAX || data->tables > U32_MAX; early_memunmap(data, sizeof(*data)); } else { over4g |= systab64->fw_vendor > U32_MAX || - systab64->runtime > U32_MAX || systab64->tables > U32_MAX; } } else { From 9cd437ac0ef4f324a92e2579784b03bb487ae7fb Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 17:23:21 +0100 Subject: [PATCH 0521/2231] efi/x86: Make fw_vendor, config_table and runtime sysfs nodes x86 specific There is some code that exposes physical addresses of certain parts of the EFI firmware implementation via sysfs nodes. These nodes are only used on x86, and are of dubious value to begin with, so let's move their handling into the x86 arch code. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 2 ++ arch/x86/kernel/kexec-bzimage64.c | 4 +-- arch/x86/platform/efi/efi.c | 60 ++++++++++++++++++++++++------- arch/x86/platform/efi/quirks.c | 2 +- drivers/firmware/efi/arm-init.c | 3 -- drivers/firmware/efi/efi.c | 40 ++++----------------- include/linux/efi.h | 3 -- 7 files changed, 59 insertions(+), 55 deletions(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index ee867f01b2f6..78fc28da2e29 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -10,6 +10,8 @@ #include #include +extern unsigned long efi_fw_vendor, efi_config_table; + /* * We map the EFI regions needed for runtime services non-contiguously, * with preserved alignment on virtual addresses starting from -4G down diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index f400678bd345..db6578d45157 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -141,8 +141,8 @@ prepare_add_efi_setup_data(struct boot_params *params, struct setup_data *sd = (void *)params + efi_setup_data_offset; struct efi_setup_data *esd = (void *)sd + sizeof(struct setup_data); - esd->fw_vendor = efi.fw_vendor; - esd->tables = efi.config_table; + esd->fw_vendor = efi_fw_vendor; + esd->tables = efi_config_table; esd->smbios = efi.smbios; sd->type = SETUP_EFI; diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 7d932452a40f..6fa412e156c7 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -59,6 +59,9 @@ static u64 efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; +static unsigned long efi_runtime, efi_nr_tables; + +unsigned long efi_fw_vendor, efi_config_table; static const efi_config_table_type_t arch_tables[] __initconst = { {EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys}, @@ -78,9 +81,9 @@ static const unsigned long * const efi_tables[] = { #ifdef CONFIG_X86_UV &uv_systab_phys, #endif - &efi.fw_vendor, - &efi.runtime, - &efi.config_table, + &efi_fw_vendor, + &efi_runtime, + &efi_config_table, &efi.esrt, &prop_phys, &efi_mem_attr_table, @@ -434,7 +437,7 @@ static int __init efi_config_init(const efi_config_table_type_t *arch_tables) void *config_tables; int sz, ret; - if (efi.systab->nr_tables == 0) + if (efi_nr_tables == 0) return 0; if (efi_enabled(EFI_64BIT)) @@ -445,17 +448,16 @@ static int __init efi_config_init(const efi_config_table_type_t *arch_tables) /* * Let's see what config tables the firmware passed to us. */ - config_tables = early_memremap(efi.systab->tables, - efi.systab->nr_tables * sz); + config_tables = early_memremap(efi_config_table, efi_nr_tables * sz); if (config_tables == NULL) { pr_err("Could not map Configuration table!\n"); return -ENOMEM; } - ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, + ret = efi_config_parse_tables(config_tables, efi_nr_tables, arch_tables); - early_memunmap(config_tables, efi.systab->nr_tables * sz); + early_memunmap(config_tables, efi_nr_tables * sz); return ret; } @@ -474,11 +476,12 @@ void __init efi_init(void) if (efi_systab_init(efi_systab_phys)) return; - efi.config_table = (unsigned long)efi.systab->tables; - efi.fw_vendor = (unsigned long)efi.systab->fw_vendor; - efi.runtime = (unsigned long)efi.systab->runtime; + efi_config_table = (unsigned long)efi.systab->tables; + efi_nr_tables = efi.systab->nr_tables; + efi_fw_vendor = (unsigned long)efi.systab->fw_vendor; + efi_runtime = (unsigned long)efi.systab->runtime; - if (efi_reuse_config(efi.systab->tables, efi.systab->nr_tables)) + if (efi_reuse_config(efi_config_table, efi_nr_tables)) return; if (efi_config_init(arch_tables)) @@ -1023,3 +1026,36 @@ char *efi_systab_show_arch(char *str) str += sprintf(str, "UGA=0x%lx\n", uga_phys); return str; } + +#define EFI_FIELD(var) efi_ ## var + +#define EFI_ATTR_SHOW(name) \ +static ssize_t name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \ +} + +EFI_ATTR_SHOW(fw_vendor); +EFI_ATTR_SHOW(runtime); +EFI_ATTR_SHOW(config_table); + +struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); +struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); +struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); + +umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + if (attr == &efi_attr_fw_vendor.attr) { + if (efi_enabled(EFI_PARAVIRT) || + efi_fw_vendor == EFI_INVALID_TABLE_ADDR) + return 0; + } else if (attr == &efi_attr_runtime.attr) { + if (efi_runtime == EFI_INVALID_TABLE_ADDR) + return 0; + } else if (attr == &efi_attr_config_table.attr) { + if (efi_config_table == EFI_INVALID_TABLE_ADDR) + return 0; + } + return attr->mode; +} diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 88d32c06cffa..b0e0161e2e8e 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -537,7 +537,7 @@ int __init efi_reuse_config(u64 tables, int nr_tables) goto out_memremap; } - for (i = 0; i < efi.systab->nr_tables; i++) { + for (i = 0; i < nr_tables; i++) { efi_guid_t guid; guid = ((efi_config_table_64_t *)p)->guid; diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index d1f44c847841..5fc2f6813b84 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -120,9 +120,6 @@ static int __init uefi_init(void) retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, arch_tables); - if (!retval) - efi.config_table = (unsigned long)efi.systab->tables; - early_memunmap(config_tables, table_size); out: early_memunmap(efi.systab, sizeof(efi_system_table_t)); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 45de4c4a696b..718dddfa0a0b 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -39,9 +39,6 @@ struct efi __read_mostly efi = { .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR, - .fw_vendor = EFI_INVALID_TABLE_ADDR, - .runtime = EFI_INVALID_TABLE_ADDR, - .config_table = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, @@ -142,55 +139,30 @@ static ssize_t systab_show(struct kobject *kobj, static struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400); -#define EFI_FIELD(var) efi.var - -#define EFI_ATTR_SHOW(name) \ -static ssize_t name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \ -} - -EFI_ATTR_SHOW(fw_vendor); -EFI_ATTR_SHOW(runtime); -EFI_ATTR_SHOW(config_table); - static ssize_t fw_platform_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); } -static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); -static struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); -static struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); +extern __weak struct kobj_attribute efi_attr_fw_vendor; +extern __weak struct kobj_attribute efi_attr_runtime; +extern __weak struct kobj_attribute efi_attr_config_table; static struct kobj_attribute efi_attr_fw_platform_size = __ATTR_RO(fw_platform_size); static struct attribute *efi_subsys_attrs[] = { &efi_attr_systab.attr, + &efi_attr_fw_platform_size.attr, &efi_attr_fw_vendor.attr, &efi_attr_runtime.attr, &efi_attr_config_table.attr, - &efi_attr_fw_platform_size.attr, NULL, }; -static umode_t efi_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int n) +umode_t __weak efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, + int n) { - if (attr == &efi_attr_fw_vendor.attr) { - if (efi_enabled(EFI_PARAVIRT) || - efi.fw_vendor == EFI_INVALID_TABLE_ADDR) - return 0; - } else if (attr == &efi_attr_runtime.attr) { - if (efi.runtime == EFI_INVALID_TABLE_ADDR) - return 0; - } else if (attr == &efi_attr_config_table.attr) { - if (efi.config_table == EFI_INVALID_TABLE_ADDR) - return 0; - } - return attr->mode; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 99a7fcbe5e9b..a42045568df3 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -535,9 +535,6 @@ extern struct efi { unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long fw_vendor; /* fw_vendor */ - unsigned long runtime; /* runtime table */ - unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ From 09308012d8546dda75e96c02bed19e2ba1e875fd Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 18:35:17 +0100 Subject: [PATCH 0522/2231] efi/x86: Merge assignments of efi.runtime_version efi.runtime_version is always set to the same value on both existing code paths, so just set it earlier from a shared one. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 6fa412e156c7..e4ee9a37254a 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -420,6 +420,8 @@ static int __init efi_systab_init(u64 phys) efi_systab.tables = systab32->tables; } + efi.runtime_version = hdr->revision; + efi_systab_report_header(hdr, efi_systab.fw_vendor); early_memunmap(p, size); @@ -870,15 +872,6 @@ static void __init kexec_enter_virtual_mode(void) } efi_sync_low_kernel_mappings(); - - /* - * Now that EFI is in virtual mode, update the function - * pointers in the runtime service table to the new virtual addresses. - * - * Call EFI services through wrapper functions. - */ - efi.runtime_version = efi_systab.hdr.revision; - efi_native_runtime_setup(); #endif } @@ -965,14 +958,6 @@ static void __init __efi_enter_virtual_mode(void) efi_free_boot_services(); - /* - * Now that EFI is in virtual mode, update the function - * pointers in the runtime service table to the new virtual addresses. - * - * Call EFI services through wrapper functions. - */ - efi.runtime_version = efi_systab.hdr.revision; - if (!efi_is_mixed()) efi_native_runtime_setup(); else From 59f2a619a2db86111e8bb30f349aebff6eb75baa Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 21 Jan 2020 09:44:43 +0100 Subject: [PATCH 0523/2231] efi: Add 'runtime' pointer to struct efi Instead of going through the EFI system table each time, just copy the runtime services table pointer into struct efi directly. This is the last use of the system table pointer in struct efi, allowing us to drop it in a future patch, along with a fair amount of quirky handling of the translated address. Note that usually, the runtime services pointer changes value during the call to SetVirtualAddressMap(), so grab the updated value as soon as that call returns. (Mixed mode uses a 1:1 mapping, and kexec boot enters with the updated address in the system table, so in those cases, we don't need to do anything here) Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 3 ++- arch/x86/kernel/asm-offsets_32.c | 5 +++++ arch/x86/platform/efi/efi.c | 9 ++++++--- arch/x86/platform/efi/efi_32.c | 13 ++++++++----- arch/x86/platform/efi/efi_64.c | 14 ++++++++------ arch/x86/platform/efi/efi_stub_32.S | 21 ++++++++++++++++----- drivers/firmware/efi/arm-init.c | 1 + drivers/firmware/efi/runtime-wrappers.c | 4 ++-- include/linux/efi.h | 1 + 9 files changed, 49 insertions(+), 22 deletions(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 78fc28da2e29..0de57151c732 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -218,7 +218,8 @@ extern void efi_thunk_runtime_setup(void); efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size, unsigned long descriptor_size, u32 descriptor_version, - efi_memory_desc_t *virtual_map); + efi_memory_desc_t *virtual_map, + unsigned long systab_phys); /* arch specific definitions used by the stub code */ diff --git a/arch/x86/kernel/asm-offsets_32.c b/arch/x86/kernel/asm-offsets_32.c index 82826f2275cc..2b4256ebe86e 100644 --- a/arch/x86/kernel/asm-offsets_32.c +++ b/arch/x86/kernel/asm-offsets_32.c @@ -3,6 +3,8 @@ # error "Please do not build this file directly, build asm-offsets.c instead" #endif +#include + #include #define __SYSCALL_I386(nr, sym, qual) [nr] = 1, @@ -64,4 +66,7 @@ void foo(void) BLANK(); DEFINE(__NR_syscall_max, sizeof(syscalls) - 1); DEFINE(NR_syscalls, sizeof(syscalls)); + + BLANK(); + DEFINE(EFI_svam, offsetof(efi_runtime_services_t, set_virtual_address_map)); } diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index e4ee9a37254a..6bd8aae235d2 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -55,8 +55,8 @@ #include static efi_system_table_t efi_systab __initdata; -static u64 efi_systab_phys __initdata; +static unsigned long efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; static unsigned long efi_runtime, efi_nr_tables; @@ -335,7 +335,7 @@ void __init efi_print_memmap(void) } } -static int __init efi_systab_init(u64 phys) +static int __init efi_systab_init(unsigned long phys) { int size = efi_enabled(EFI_64BIT) ? sizeof(efi_system_table_64_t) : sizeof(efi_system_table_32_t); @@ -949,7 +949,8 @@ static void __init __efi_enter_virtual_mode(void) status = efi_set_virtual_address_map(efi.memmap.desc_size * count, efi.memmap.desc_size, efi.memmap.desc_version, - (efi_memory_desc_t *)pa); + (efi_memory_desc_t *)pa, + efi_systab_phys); if (status != EFI_SUCCESS) { pr_err("Unable to switch EFI into virtual mode (status=%lx)!\n", status); @@ -983,6 +984,8 @@ void __init efi_enter_virtual_mode(void) if (efi_enabled(EFI_PARAVIRT)) return; + efi.runtime = (efi_runtime_services_t *)efi_runtime; + if (efi_setup) kexec_enter_virtual_mode(); else diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c index 081d466002c9..c049c432745d 100644 --- a/arch/x86/platform/efi/efi_32.c +++ b/arch/x86/platform/efi/efi_32.c @@ -66,14 +66,16 @@ void __init efi_map_region(efi_memory_desc_t *md) void __init efi_map_region_fixed(efi_memory_desc_t *md) {} void __init parse_efi_setup(u64 phys_addr, u32 data_len) {} -efi_status_t efi_call_svam(efi_set_virtual_address_map_t *__efiapi *, - u32, u32, u32, void *); +efi_status_t efi_call_svam(efi_runtime_services_t * const *, + u32, u32, u32, void *, u32); efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size, unsigned long descriptor_size, u32 descriptor_version, - efi_memory_desc_t *virtual_map) + efi_memory_desc_t *virtual_map, + unsigned long systab_phys) { + const efi_system_table_t *systab = (efi_system_table_t *)systab_phys; struct desc_ptr gdt_descr; efi_status_t status; unsigned long flags; @@ -90,9 +92,10 @@ efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size, /* Disable interrupts around EFI calls: */ local_irq_save(flags); - status = efi_call_svam(&efi.systab->runtime->set_virtual_address_map, + status = efi_call_svam(&systab->runtime, memory_map_size, descriptor_size, - descriptor_version, virtual_map); + descriptor_version, virtual_map, + __pa(&efi.runtime)); local_irq_restore(flags); load_fixmap_gdt(0); diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index fa8506e76bbe..f78f7da666fb 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -500,12 +500,9 @@ static DEFINE_SPINLOCK(efi_runtime_lock); */ #define __efi_thunk(func, ...) \ ({ \ - efi_runtime_services_32_t *__rt; \ unsigned short __ds, __es; \ efi_status_t ____s; \ \ - __rt = (void *)(unsigned long)efi.systab->mixed_mode.runtime; \ - \ savesegment(ds, __ds); \ savesegment(es, __es); \ \ @@ -513,7 +510,7 @@ static DEFINE_SPINLOCK(efi_runtime_lock); loadsegment(ds, __KERNEL_DS); \ loadsegment(es, __KERNEL_DS); \ \ - ____s = efi64_thunk(__rt->func, __VA_ARGS__); \ + ____s = efi64_thunk(efi.runtime->mixed_mode.func, __VA_ARGS__); \ \ loadsegment(ds, __ds); \ loadsegment(es, __es); \ @@ -886,8 +883,10 @@ efi_status_t __init __no_sanitize_address efi_set_virtual_address_map(unsigned long memory_map_size, unsigned long descriptor_size, u32 descriptor_version, - efi_memory_desc_t *virtual_map) + efi_memory_desc_t *virtual_map, + unsigned long systab_phys) { + const efi_system_table_t *systab = (efi_system_table_t *)systab_phys; efi_status_t status; unsigned long flags; pgd_t *save_pgd = NULL; @@ -910,13 +909,16 @@ efi_set_virtual_address_map(unsigned long memory_map_size, /* Disable interrupts around EFI calls: */ local_irq_save(flags); - status = efi_call(efi.systab->runtime->set_virtual_address_map, + status = efi_call(efi.runtime->set_virtual_address_map, memory_map_size, descriptor_size, descriptor_version, virtual_map); local_irq_restore(flags); kernel_fpu_end(); + /* grab the virtually remapped EFI runtime services table pointer */ + efi.runtime = READ_ONCE(systab->runtime); + if (save_pgd) efi_uv1_memmap_phys_epilog(save_pgd); else diff --git a/arch/x86/platform/efi/efi_stub_32.S b/arch/x86/platform/efi/efi_stub_32.S index 75c46e7a809f..09237236fb25 100644 --- a/arch/x86/platform/efi/efi_stub_32.S +++ b/arch/x86/platform/efi/efi_stub_32.S @@ -8,14 +8,20 @@ #include #include +#include #include __INIT SYM_FUNC_START(efi_call_svam) - push 8(%esp) - push 8(%esp) + push %ebp + movl %esp, %ebp + push %ebx + + push 16(%esp) + push 16(%esp) push %ecx push %edx + movl %eax, %ebx // &systab_phys->runtime /* * Switch to the flat mapped alias of this routine, by jumping to the @@ -35,15 +41,20 @@ SYM_FUNC_START(efi_call_svam) subl $__PAGE_OFFSET, %esp /* call the EFI routine */ - call *(%eax) + movl (%eax), %eax + call *EFI_svam(%eax) - /* convert ESP back to a kernel VA, and pop the outgoing args */ - addl $__PAGE_OFFSET + 16, %esp + /* grab the virtually remapped EFI runtime services table pointer */ + movl (%ebx), %ecx + movl 36(%esp), %edx // &efi.runtime + movl %ecx, (%edx) /* re-enable paging */ movl %cr0, %edx orl $0x80000000, %edx movl %edx, %cr0 + pop %ebx + leave ret SYM_FUNC_END(efi_call_svam) diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 5fc2f6813b84..77048f7a9659 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -104,6 +104,7 @@ static int __init uefi_init(void) if (retval) goto out; + efi.runtime = efi.systab->runtime; efi.runtime_version = efi.systab->hdr.revision; efi_systab_report_header(&efi.systab->hdr, diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 65fffaa22210..1410beaef5c3 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -40,9 +40,9 @@ * code doesn't get too cluttered: */ #define efi_call_virt(f, args...) \ - efi_call_virt_pointer(efi.systab->runtime, f, args) + efi_call_virt_pointer(efi.runtime, f, args) #define __efi_call_virt(f, args...) \ - __efi_call_virt_pointer(efi.systab->runtime, f, args) + __efi_call_virt_pointer(efi.runtime, f, args) struct efi_runtime_work efi_rts_work; diff --git a/include/linux/efi.h b/include/linux/efi.h index a42045568df3..1f69c4c2dd5c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -529,6 +529,7 @@ typedef struct { * All runtime access to EFI goes through this structure: */ extern struct efi { + const efi_runtime_services_t *runtime; /* EFI runtime services table */ efi_system_table_t *systab; /* EFI system table */ unsigned int runtime_version; /* Runtime services version */ unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ From 8819ba39661efec88efd11610988424cb1bf99f8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 17:39:39 +0100 Subject: [PATCH 0524/2231] efi/arm: Drop unnecessary references to efi.systab Instead of populating efi.systab very early during efi_init() with a mapping that is released again before the function exits, use a local variable here. Now that we use efi.runtime to access the runtime services table, this removes the only reference efi.systab, so there is no need to populate it anymore, or discover its virtually remapped address. So drop the references entirely. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/arm-init.c | 33 +++++++++++++----------------- drivers/firmware/efi/arm-runtime.c | 18 ---------------- 2 files changed, 14 insertions(+), 37 deletions(-) diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 77048f7a9659..76bf5b22e49e 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -22,8 +22,6 @@ #include -u64 efi_system_table; - static int __init is_memory(efi_memory_desc_t *md) { if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC)) @@ -36,7 +34,7 @@ static int __init is_memory(efi_memory_desc_t *md) * as some data members of the EFI system table are virtually remapped after * SetVirtualAddressMap() has been called. */ -static phys_addr_t efi_to_phys(unsigned long addr) +static phys_addr_t __init efi_to_phys(unsigned long addr) { efi_memory_desc_t *md; @@ -83,15 +81,15 @@ static void __init init_screen_info(void) memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size); } -static int __init uefi_init(void) +static int __init uefi_init(u64 efi_system_table) { efi_config_table_t *config_tables; + efi_system_table_t *systab; size_t table_size; int retval; - efi.systab = early_memremap_ro(efi_system_table, - sizeof(efi_system_table_t)); - if (efi.systab == NULL) { + systab = early_memremap_ro(efi_system_table, sizeof(efi_system_table_t)); + if (systab == NULL) { pr_warn("Unable to map EFI system table.\n"); return -ENOMEM; } @@ -100,30 +98,29 @@ static int __init uefi_init(void) if (IS_ENABLED(CONFIG_64BIT)) set_bit(EFI_64BIT, &efi.flags); - retval = efi_systab_check_header(&efi.systab->hdr, 2); + retval = efi_systab_check_header(&systab->hdr, 2); if (retval) goto out; - efi.runtime = efi.systab->runtime; - efi.runtime_version = efi.systab->hdr.revision; + efi.runtime = systab->runtime; + efi.runtime_version = systab->hdr.revision; - efi_systab_report_header(&efi.systab->hdr, - efi_to_phys(efi.systab->fw_vendor)); + efi_systab_report_header(&systab->hdr, efi_to_phys(systab->fw_vendor)); - table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables; - config_tables = early_memremap_ro(efi_to_phys(efi.systab->tables), + table_size = sizeof(efi_config_table_t) * systab->nr_tables; + config_tables = early_memremap_ro(efi_to_phys(systab->tables), table_size); if (config_tables == NULL) { pr_warn("Unable to map EFI config table array.\n"); retval = -ENOMEM; goto out; } - retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, + retval = efi_config_parse_tables(config_tables, systab->nr_tables, arch_tables); early_memunmap(config_tables, table_size); out: - early_memunmap(efi.systab, sizeof(efi_system_table_t)); + early_memunmap(systab, sizeof(efi_system_table_t)); return retval; } @@ -214,8 +211,6 @@ void __init efi_init(void) if (!efi_get_fdt_params(¶ms)) return; - efi_system_table = params.system_table; - data.desc_version = params.desc_ver; data.desc_size = params.desc_size; data.size = params.mmap_size; @@ -234,7 +229,7 @@ void __init efi_init(void) "Unexpected EFI_MEMORY_DESCRIPTOR version %ld", efi.memmap.desc_version); - if (uefi_init() < 0) { + if (uefi_init(params.system_table) < 0) { efi_memmap_unmap(); return; } diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c index 9dda2602c862..b876373f2297 100644 --- a/drivers/firmware/efi/arm-runtime.c +++ b/drivers/firmware/efi/arm-runtime.c @@ -25,8 +25,6 @@ #include #include -extern u64 efi_system_table; - #if defined(CONFIG_PTDUMP_DEBUGFS) && defined(CONFIG_ARM64) #include @@ -54,13 +52,11 @@ device_initcall(ptdump_init); static bool __init efi_virtmap_init(void) { efi_memory_desc_t *md; - bool systab_found; efi_mm.pgd = pgd_alloc(&efi_mm); mm_init_cpumask(&efi_mm); init_new_context(NULL, &efi_mm); - systab_found = false; for_each_efi_memory_desc(md) { phys_addr_t phys = md->phys_addr; int ret; @@ -76,20 +72,6 @@ static bool __init efi_virtmap_init(void) &phys, ret); return false; } - /* - * If this entry covers the address of the UEFI system table, - * calculate and record its virtual address. - */ - if (efi_system_table >= phys && - efi_system_table < phys + (md->num_pages * EFI_PAGE_SIZE)) { - efi.systab = (void *)(unsigned long)(efi_system_table - - phys + md->virt_addr); - systab_found = true; - } - } - if (!systab_found) { - pr_err("No virtual mapping found for the UEFI System Table\n"); - return false; } if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions)) From fd26830423e5f7442001f090cd4a53f4b6c3d9fa Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 21 Jan 2020 10:16:32 +0100 Subject: [PATCH 0525/2231] efi/x86: Drop 'systab' member from struct efi The systab member in struct efi has outlived its usefulness, now that we have better ways to access the only piece of information we are interested in after init, which is the EFI runtime services table address. So instead of instantiating a doctored copy at early boot with lots of mangled values, and switching the pointer when switching into virtual mode, let's grab the values we need directly, and get rid of the systab pointer entirely. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 87 ++++++------------------------------- include/linux/efi.h | 52 +++++++++++----------- 2 files changed, 41 insertions(+), 98 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 6bd8aae235d2..43b24e149312 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -54,8 +54,6 @@ #include #include -static efi_system_table_t efi_systab __initdata; - static unsigned long efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; @@ -359,28 +357,8 @@ static int __init efi_systab_init(unsigned long phys) if (efi_enabled(EFI_64BIT)) { const efi_system_table_64_t *systab64 = p; - efi_systab.hdr = systab64->hdr; - efi_systab.fw_vendor = systab64->fw_vendor; - efi_systab.fw_revision = systab64->fw_revision; - efi_systab.con_in_handle = systab64->con_in_handle; - efi_systab.con_in = systab64->con_in; - efi_systab.con_out_handle = systab64->con_out_handle; - efi_systab.con_out = (void *)(unsigned long)systab64->con_out; - efi_systab.stderr_handle = systab64->stderr_handle; - efi_systab.stderr = systab64->stderr; - efi_systab.runtime = (void *)(unsigned long)systab64->runtime; - efi_systab.boottime = (void *)(unsigned long)systab64->boottime; - efi_systab.nr_tables = systab64->nr_tables; - efi_systab.tables = systab64->tables; - - over4g = systab64->con_in_handle > U32_MAX || - systab64->con_in > U32_MAX || - systab64->con_out_handle > U32_MAX || - systab64->con_out > U32_MAX || - systab64->stderr_handle > U32_MAX || - systab64->stderr > U32_MAX || - systab64->runtime > U32_MAX || - systab64->boottime > U32_MAX; + efi_runtime = systab64->runtime; + over4g = systab64->runtime > U32_MAX; if (efi_setup) { struct efi_setup_data *data; @@ -391,38 +369,33 @@ static int __init efi_systab_init(unsigned long phys) return -ENOMEM; } - efi_systab.fw_vendor = (unsigned long)data->fw_vendor; - efi_systab.tables = (unsigned long)data->tables; + efi_fw_vendor = (unsigned long)data->fw_vendor; + efi_config_table = (unsigned long)data->tables; over4g |= data->fw_vendor > U32_MAX || data->tables > U32_MAX; early_memunmap(data, sizeof(*data)); } else { + efi_fw_vendor = systab64->fw_vendor; + efi_config_table = systab64->tables; + over4g |= systab64->fw_vendor > U32_MAX || systab64->tables > U32_MAX; } + efi_nr_tables = systab64->nr_tables; } else { const efi_system_table_32_t *systab32 = p; - efi_systab.hdr = systab32->hdr; - efi_systab.fw_vendor = systab32->fw_vendor; - efi_systab.fw_revision = systab32->fw_revision; - efi_systab.con_in_handle = systab32->con_in_handle; - efi_systab.con_in = systab32->con_in; - efi_systab.con_out_handle = systab32->con_out_handle; - efi_systab.con_out = (void *)(unsigned long)systab32->con_out; - efi_systab.stderr_handle = systab32->stderr_handle; - efi_systab.stderr = systab32->stderr; - efi_systab.runtime = (void *)(unsigned long)systab32->runtime; - efi_systab.boottime = (void *)(unsigned long)systab32->boottime; - efi_systab.nr_tables = systab32->nr_tables; - efi_systab.tables = systab32->tables; + efi_fw_vendor = systab32->fw_vendor; + efi_runtime = systab32->runtime; + efi_config_table = systab32->tables; + efi_nr_tables = systab32->nr_tables; } efi.runtime_version = hdr->revision; - efi_systab_report_header(hdr, efi_systab.fw_vendor); + efi_systab_report_header(hdr, efi_fw_vendor); early_memunmap(p, size); if (IS_ENABLED(CONFIG_X86_32) && over4g) { @@ -430,7 +403,6 @@ static int __init efi_systab_init(unsigned long phys) return -EINVAL; } - efi.systab = &efi_systab; return 0; } @@ -478,11 +450,6 @@ void __init efi_init(void) if (efi_systab_init(efi_systab_phys)) return; - efi_config_table = (unsigned long)efi.systab->tables; - efi_nr_tables = efi.systab->nr_tables; - efi_fw_vendor = (unsigned long)efi.systab->fw_vendor; - efi_runtime = (unsigned long)efi.systab->runtime; - if (efi_reuse_config(efi_config_table, efi_nr_tables)) return; @@ -624,20 +591,6 @@ static void __init efi_merge_regions(void) } } -static void __init get_systab_virt_addr(efi_memory_desc_t *md) -{ - unsigned long size; - u64 end, systab; - - size = md->num_pages << EFI_PAGE_SHIFT; - end = md->phys_addr + size; - systab = efi_systab_phys; - if (md->phys_addr <= systab && systab < end) { - systab += md->virt_addr - md->phys_addr; - efi.systab = (efi_system_table_t *)(unsigned long)systab; - } -} - static void *realloc_pages(void *old_memmap, int old_shift) { void *ret; @@ -793,7 +746,6 @@ static void * __init efi_map_regions(int *count, int *pg_shift) continue; efi_map_region(md); - get_systab_virt_addr(md); if (left < desc_size) { new_memmap = realloc_pages(new_memmap, *pg_shift); @@ -819,8 +771,6 @@ static void __init kexec_enter_virtual_mode(void) efi_memory_desc_t *md; unsigned int num_pages; - efi.systab = NULL; - /* * We don't do virtual mode, since we don't do runtime services, on * non-native EFI. With the UV1 memmap, we don't do runtime services in @@ -843,10 +793,8 @@ static void __init kexec_enter_virtual_mode(void) * Map efi regions which were passed via setup_data. The virt_addr is a * fixed addr which was used in first kernel of a kexec boot. */ - for_each_efi_memory_desc(md) { + for_each_efi_memory_desc(md) efi_map_region_fixed(md); /* FIXME: add error handling */ - get_systab_virt_addr(md); - } /* * Unregister the early EFI memmap from efi_init() and install @@ -861,8 +809,6 @@ static void __init kexec_enter_virtual_mode(void) return; } - BUG_ON(!efi.systab); - num_pages = ALIGN(efi.memmap.nr_map * efi.memmap.desc_size, PAGE_SIZE); num_pages >>= PAGE_SHIFT; @@ -905,8 +851,6 @@ static void __init __efi_enter_virtual_mode(void) efi_status_t status; unsigned long pa; - efi.systab = NULL; - if (efi_alloc_page_tables()) { pr_err("Failed to allocate EFI page tables\n"); goto err; @@ -938,9 +882,6 @@ static void __init __efi_enter_virtual_mode(void) efi_print_memmap(); } - if (WARN_ON(!efi.systab)) - goto err; - if (efi_setup_page_tables(pa, 1 << pg_shift)) goto err; diff --git a/include/linux/efi.h b/include/linux/efi.h index 1f69c4c2dd5c..575e6aa39514 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -530,31 +530,33 @@ typedef struct { */ extern struct efi { const efi_runtime_services_t *runtime; /* EFI runtime services table */ - efi_system_table_t *systab; /* EFI system table */ - unsigned int runtime_version; /* Runtime services version */ - unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ - unsigned long acpi20; /* ACPI table (ACPI 2.0) */ - unsigned long smbios; /* SMBIOS table (32 bit entry point) */ - unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long esrt; /* ESRT table */ - unsigned long tpm_log; /* TPM2 Event Log table */ - unsigned long tpm_final_log; /* TPM2 Final Events Log table */ - efi_get_time_t *get_time; - efi_set_time_t *set_time; - efi_get_wakeup_time_t *get_wakeup_time; - efi_set_wakeup_time_t *set_wakeup_time; - efi_get_variable_t *get_variable; - efi_get_next_variable_t *get_next_variable; - efi_set_variable_t *set_variable; - efi_set_variable_t *set_variable_nonblocking; - efi_query_variable_info_t *query_variable_info; - efi_query_variable_info_t *query_variable_info_nonblocking; - efi_update_capsule_t *update_capsule; - efi_query_capsule_caps_t *query_capsule_caps; - efi_get_next_high_mono_count_t *get_next_high_mono_count; - efi_reset_system_t *reset_system; - struct efi_memory_map memmap; - unsigned long flags; + unsigned int runtime_version; /* Runtime services version */ + + unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ + unsigned long acpi20; /* ACPI table (ACPI 2.0) */ + unsigned long smbios; /* SMBIOS table (32 bit entry point) */ + unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ + unsigned long esrt; /* ESRT table */ + unsigned long tpm_log; /* TPM2 Event Log table */ + unsigned long tpm_final_log; /* TPM2 Final Events Log table */ + + efi_get_time_t *get_time; + efi_set_time_t *set_time; + efi_get_wakeup_time_t *get_wakeup_time; + efi_set_wakeup_time_t *set_wakeup_time; + efi_get_variable_t *get_variable; + efi_get_next_variable_t *get_next_variable; + efi_set_variable_t *set_variable; + efi_set_variable_t *set_variable_nonblocking; + efi_query_variable_info_t *query_variable_info; + efi_query_variable_info_t *query_variable_info_nonblocking; + efi_update_capsule_t *update_capsule; + efi_query_capsule_caps_t *query_capsule_caps; + efi_get_next_high_mono_count_t *get_next_high_mono_count; + efi_reset_system_t *reset_system; + + struct efi_memory_map memmap; + unsigned long flags; } efi; extern struct mm_struct efi_mm; From 223e3ee56f77570157aba8cc550208af430a869b Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 22 Feb 2020 15:15:50 +0100 Subject: [PATCH 0526/2231] efi/x86: add headroom to decompressor BSS to account for setup block In the bootparams struct, init_size defines the static footprint of the bzImage, counted from the start of the kernel image, i.e., startup_32(). The PE/COFF metadata declares the same size for the entire image, but this time, the image includes the setup block as well, and so the space reserved by UEFI is a bit too small. This usually doesn't matter, since we normally relocate the kernel into a memory allocation of the correct size. But in the unlikely case that the image happens to be loaded at exactly the preferred offset, we skip this relocation, and execute the image in place, stepping on memory beyond the provided allocation, which may be in use for other purposes. Let's fix this by adding the size of the setup block to the image size as declared in the PE/COFF header. Signed-off-by: Ard Biesheuvel --- arch/x86/boot/tools/build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c index 55e669d29e54..c08db2ee4ba2 100644 --- a/arch/x86/boot/tools/build.c +++ b/arch/x86/boot/tools/build.c @@ -408,7 +408,7 @@ int main(int argc, char ** argv) update_pecoff_text(setup_sectors * 512, i + (sys_size * 16)); init_sz = get_unaligned_le32(&buf[0x260]); - update_pecoff_bss(i + (sys_size * 16), init_sz); + update_pecoff_bss(i + (sys_size * 16), init_sz + setup_sectors * 512); efi_stub_entry_update(); From 832187f03994b03b6cc4a3b9130d82b1ec5cbec4 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 12 Feb 2020 11:26:10 +0100 Subject: [PATCH 0527/2231] efi/x86: Drop redundant .bss section In commit c7fb93ec51d462ec ("x86/efi: Include a .bss section within the PE/COFF headers") we added a separate .bss section to the PE/COFF header of the compressed kernel describing the static memory footprint of the decompressor, to ensure that it has enough headroom to decompress itself. We can achieve the exact same result by increasing the virtual size of the .text section, without changing the raw size, which, as per the PE/COFF specification, requires the loader to zero initialize the delta. Doing so frees up a slot in the section table, which we will use later to describe the mixed mode entrypoint. Signed-off-by: Ard Biesheuvel --- arch/x86/boot/header.S | 21 +-------------------- arch/x86/boot/tools/build.c | 36 ++++++++++++------------------------ 2 files changed, 13 insertions(+), 44 deletions(-) diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index 97d9b6d6c1af..d59f6604bb42 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -106,7 +106,7 @@ coff_header: #else .word 0x8664 # x86-64 #endif - .word 4 # nr_sections + .word 3 # nr_sections .long 0 # TimeDateStamp .long 0 # PointerToSymbolTable .long 1 # NumberOfSymbols @@ -248,25 +248,6 @@ section_table: .word 0 # NumberOfLineNumbers .long 0x60500020 # Characteristics (section flags) - # - # The offset & size fields are filled in by build.c. - # - .ascii ".bss" - .byte 0 - .byte 0 - .byte 0 - .byte 0 - .long 0 - .long 0x0 - .long 0 # Size of initialized data - # on disk - .long 0x0 - .long 0 # PointerToRelocations - .long 0 # PointerToLineNumbers - .word 0 # NumberOfRelocations - .word 0 # NumberOfLineNumbers - .long 0xc8000080 # Characteristics (section flags) - #endif /* CONFIG_EFI_STUB */ # Kernel attributes; used by setup. This is part 1 of the diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c index c08db2ee4ba2..f9f5761c5d05 100644 --- a/arch/x86/boot/tools/build.c +++ b/arch/x86/boot/tools/build.c @@ -203,10 +203,12 @@ static void update_pecoff_setup_and_reloc(unsigned int size) put_unaligned_le32(10, &buf[reloc_offset + 4]); } -static void update_pecoff_text(unsigned int text_start, unsigned int file_sz) +static void update_pecoff_text(unsigned int text_start, unsigned int file_sz, + unsigned int init_sz) { unsigned int pe_header; unsigned int text_sz = file_sz - text_start; + unsigned int bss_sz = init_sz + text_start - file_sz; pe_header = get_unaligned_le32(&buf[0x3c]); @@ -214,30 +216,18 @@ static void update_pecoff_text(unsigned int text_start, unsigned int file_sz) * Size of code: Subtract the size of the first sector (512 bytes) * which includes the header. */ - put_unaligned_le32(file_sz - 512, &buf[pe_header + 0x1c]); + put_unaligned_le32(file_sz - 512 + bss_sz, &buf[pe_header + 0x1c]); + + /* Size of image */ + put_unaligned_le32(init_sz + text_start, &buf[pe_header + 0x50]); /* * Address of entry point for PE/COFF executable */ put_unaligned_le32(text_start + efi_pe_entry, &buf[pe_header + 0x28]); - update_pecoff_section_header(".text", text_start, text_sz); -} - -static void update_pecoff_bss(unsigned int file_sz, unsigned int init_sz) -{ - unsigned int pe_header; - unsigned int bss_sz = init_sz - file_sz; - - pe_header = get_unaligned_le32(&buf[0x3c]); - - /* Size of uninitialized data */ - put_unaligned_le32(bss_sz, &buf[pe_header + 0x24]); - - /* Size of image */ - put_unaligned_le32(init_sz, &buf[pe_header + 0x50]); - - update_pecoff_section_header_fields(".bss", file_sz, bss_sz, 0, 0); + update_pecoff_section_header_fields(".text", text_start, text_sz + bss_sz, + text_sz, text_start); } static int reserve_pecoff_reloc_section(int c) @@ -278,9 +268,8 @@ static void efi_stub_entry_update(void) static inline void update_pecoff_setup_and_reloc(unsigned int size) {} static inline void update_pecoff_text(unsigned int text_start, - unsigned int file_sz) {} -static inline void update_pecoff_bss(unsigned int file_sz, - unsigned int init_sz) {} + unsigned int file_sz, + unsigned int init_sz) {} static inline void efi_stub_defaults(void) {} static inline void efi_stub_entry_update(void) {} @@ -406,9 +395,8 @@ int main(int argc, char ** argv) buf[0x1f1] = setup_sectors-1; put_unaligned_le32(sys_size, &buf[0x1f4]); - update_pecoff_text(setup_sectors * 512, i + (sys_size * 16)); init_sz = get_unaligned_le32(&buf[0x260]); - update_pecoff_bss(i + (sys_size * 16), init_sz + setup_sectors * 512); + update_pecoff_text(setup_sectors * 512, i + (sys_size * 16), init_sz); efi_stub_entry_update(); From f7b85b33eb0b3025830a102b01e6e1c3426cdf13 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 14 Feb 2020 14:29:21 +0100 Subject: [PATCH 0528/2231] efi/libstub/x86: Make loaded_image protocol handling mixed mode safe Add the definitions and use the special wrapper so that the loaded_image UEFI protocol can be safely used from mixed mode. Signed-off-by: Ard Biesheuvel --- .../firmware/efi/libstub/efi-stub-helper.c | 4 +- drivers/firmware/efi/libstub/efistub.h | 45 +++++++++++++------ drivers/firmware/efi/libstub/x86-stub.c | 4 +- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index b1da58141a4d..9f34c7242939 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -171,8 +171,8 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, const u16 *s2; u8 *s1 = NULL; unsigned long cmdline_addr = 0; - int load_options_chars = image->load_options_size / 2; /* UTF-16 */ - const u16 *options = image->load_options; + int load_options_chars = efi_table_attr(image, load_options_size) / 2; + const u16 *options = efi_table_attr(image, load_options); int options_bytes = 0; /* UTF-8 bytes */ int options_chars = 0; /* UTF-16 chars */ efi_status_t status; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 2e5e79edb4d7..6960e730f990 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -308,20 +308,37 @@ union efi_graphics_output_protocol { } mixed_mode; }; -typedef struct { - u32 revision; - efi_handle_t parent_handle; - efi_system_table_t *system_table; - efi_handle_t device_handle; - void *file_path; - void *reserved; - u32 load_options_size; - void *load_options; - void *image_base; - __aligned_u64 image_size; - unsigned int image_code_type; - unsigned int image_data_type; - efi_status_t (__efiapi *unload)(efi_handle_t image_handle); +typedef union { + struct { + u32 revision; + efi_handle_t parent_handle; + efi_system_table_t *system_table; + efi_handle_t device_handle; + void *file_path; + void *reserved; + u32 load_options_size; + void *load_options; + void *image_base; + __aligned_u64 image_size; + unsigned int image_code_type; + unsigned int image_data_type; + efi_status_t (__efiapi *unload)(efi_handle_t image_handle); + }; + struct { + u32 revision; + u32 parent_handle; + u32 system_table; + u32 device_handle; + u32 file_path; + u32 reserved; + u32 load_options_size; + u32 load_options; + u32 image_base; + __aligned_u64 image_size; + u32 image_code_type; + u32 image_data_type; + u32 unload; + } mixed_mode; } efi_loaded_image_t; typedef struct { diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 7d4866471f86..ce0c3caa3087 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -377,7 +377,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, return status; } - hdr = &((struct boot_params *)image->image_base)->hdr; + hdr = &((struct boot_params *)efi_table_attr(image, image_base))->hdr; above4g = hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G; status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, @@ -392,7 +392,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, hdr = &boot_params->hdr; /* Copy the second sector to boot_params */ - memcpy(&hdr->jump, image->image_base + 512, 512); + memcpy(&hdr->jump, efi_table_attr(image, image_base) + 512, 512); /* * Fill out some of the header fields ourselves because the From 3b8f44fc0810d51b58837cf7fdba3f72f8cffcdc Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 16 Feb 2020 00:03:25 +0100 Subject: [PATCH 0529/2231] efi/libstub/x86: Use Exit() boot service to exit the stub on errors Currently, we either return with an error [from efi_pe_entry()] or enter a deadloop [in efi_main()] if any fatal errors occur during execution of the EFI stub. Let's switch to calling the Exit() EFI boot service instead in both cases, so that we a) can get rid of the deadloop, and simply return to the boot manager if any errors occur during execution of the stub, including during the call to ExitBootServices(), b) can also return cleanly from efi_pe_entry() or efi_main() in mixed mode, once we introduce support for LoadImage/StartImage based mixed mode in the next patch. Note that on systems running downstream GRUBs [which do not use LoadImage or StartImage to boot the kernel, and instead, pass their own image handle as the loaded image handle], calling Exit() will exit from GRUB rather than from the kernel, but this is a tolerable side effect. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 8 ++++++++ drivers/firmware/efi/libstub/efistub.h | 5 ++++- drivers/firmware/efi/libstub/x86-stub.c | 20 +++++++++++++------- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 0de57151c732..cdcf48d52a12 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -270,6 +270,11 @@ static inline void *efi64_zero_upper(void *p) return p; } +static inline u32 efi64_convert_status(efi_status_t status) +{ + return (u32)(status | (u64)status >> 32); +} + #define __efi64_argmap_free_pages(addr, size) \ ((addr), 0, (size)) @@ -288,6 +293,9 @@ static inline void *efi64_zero_upper(void *p) #define __efi64_argmap_locate_device_path(protocol, path, handle) \ ((protocol), (path), efi64_zero_upper(handle)) +#define __efi64_argmap_exit(handle, status, size, data) \ + ((handle), efi64_convert_status(status), (size), (data)) + /* PCI I/O */ #define __efi64_argmap_get_location(protocol, seg, bus, dev, func) \ ((protocol), efi64_zero_upper(seg), efi64_zero_upper(bus), \ diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 6960e730f990..cc90a748bcf0 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -144,7 +144,10 @@ union efi_boot_services { void *); void *load_image; void *start_image; - void *exit; + efi_status_t __noreturn (__efiapi *exit)(efi_handle_t, + efi_status_t, + unsigned long, + efi_char16_t *); void *unload_image; efi_status_t (__efiapi *exit_boot_services)(efi_handle_t, unsigned long); diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index ce0c3caa3087..cec6baa14d5c 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -340,6 +340,13 @@ static void setup_graphics(struct boot_params *boot_params) } } + +static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status) +{ + efi_bs_call(exit, handle, status, 0, NULL); + unreachable(); +} + void startup_32(struct boot_params *boot_params); void __noreturn efi_stub_entry(efi_handle_t handle, @@ -369,12 +376,12 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, /* Check if we were booted by the EFI firmware */ if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) - return EFI_INVALID_PARAMETER; + efi_exit(handle, EFI_INVALID_PARAMETER); status = efi_bs_call(handle_protocol, handle, &proto, (void *)&image); if (status != EFI_SUCCESS) { efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); - return status; + efi_exit(handle, status); } hdr = &((struct boot_params *)efi_table_attr(image, image_base))->hdr; @@ -384,7 +391,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, above4g ? ULONG_MAX : UINT_MAX); if (status != EFI_SUCCESS) { efi_printk("Failed to allocate lowmem for boot params\n"); - return status; + efi_exit(handle, status); } memset(boot_params, 0x0, 0x4000); @@ -442,7 +449,7 @@ fail2: fail: efi_free(0x4000, (unsigned long)boot_params); - return status; + efi_exit(handle, status); } static void add_e820ext(struct boot_params *params, @@ -709,7 +716,7 @@ struct boot_params *efi_main(efi_handle_t handle, /* Check if we were booted by the EFI firmware */ if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) - goto fail; + efi_exit(handle, EFI_INVALID_PARAMETER); /* * If the kernel isn't already loaded at the preferred load @@ -793,6 +800,5 @@ struct boot_params *efi_main(efi_handle_t handle, fail: efi_printk("efi_main() failed!\n"); - for (;;) - asm("hlt"); + efi_exit(handle, status); } From 17054f492dfd4d91e093ebb87013807812ec42a4 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 12 Feb 2020 23:20:54 +0100 Subject: [PATCH 0530/2231] efi/x86: Implement mixed mode boot without the handover protocol Add support for booting 64-bit x86 kernels from 32-bit firmware running on 64-bit capable CPUs without requiring the bootloader to implement the EFI handover protocol or allocate the setup block, etc etc, all of which can be done by the stub itself, using code that already exists. Instead, create an ordinary EFI application entrypoint but implemented in 32-bit code [so that it can be invoked by 32-bit firmware], and stash the address of this 32-bit entrypoint in the .compat section where the bootloader can find it. Note that we use the setup block embedded in the binary to go through startup_32(), but it gets reallocated and copied in efi_pe_entry(), using the same code that runs when the x86 kernel is booted in EFI mode from native firmware. This requires the loaded image protocol to be installed on the kernel image's EFI handle, and point to the kernel image itself and not to its loader. This, in turn, requires the bootloader to use the LoadImage() boot service to load the 64-bit image from 32-bit firmware, which is in fact supported by firmware based on EDK2. (Only StartImage() will fail, and instead, the newly added entrypoint needs to be invoked) Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/head_64.S | 59 ++++++++++++++++++++++++- drivers/firmware/efi/libstub/x86-stub.c | 29 ++++++------ 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index a4f5561c1c0e..f7bacc4c1494 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -207,8 +207,13 @@ SYM_FUNC_START(startup_32) cmp $0, %edi jz 1f leal efi64_stub_entry(%ebp), %eax - movl %esi, %edx movl efi32_boot_args+4(%ebp), %esi + movl efi32_boot_args+8(%ebp), %edx // saved bootparams pointer + cmpl $0, %edx + jnz 1f + leal efi_pe_entry(%ebp), %eax + movl %edi, %ecx // MS calling convention + movl %esi, %edx 1: #endif pushl %eax @@ -233,6 +238,8 @@ SYM_FUNC_START(efi32_stub_entry) 1: pop %ebp subl $1b, %ebp + movl %esi, efi32_boot_args+8(%ebp) +SYM_INNER_LABEL(efi32_pe_stub_entry, SYM_L_LOCAL) movl %ecx, efi32_boot_args(%ebp) movl %edx, efi32_boot_args+4(%ebp) movb $0, efi_is64(%ebp) @@ -641,8 +648,56 @@ SYM_DATA_START_LOCAL(gdt) SYM_DATA_END_LABEL(gdt, SYM_L_LOCAL, gdt_end) #ifdef CONFIG_EFI_MIXED -SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0) +SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0) SYM_DATA(efi_is64, .byte 1) + +#define ST32_boottime 60 // offsetof(efi_system_table_32_t, boottime) +#define BS32_handle_protocol 88 // offsetof(efi_boot_services_32_t, handle_protocol) +#define LI32_image_base 32 // offsetof(efi_loaded_image_32_t, image_base) + + .text + .code32 +SYM_FUNC_START(efi32_pe_entry) + pushl %ebp + + call verify_cpu // check for long mode support + testl %eax, %eax + movl $0x80000003, %eax // EFI_UNSUPPORTED + jnz 3f + + call 1f +1: pop %ebp + subl $1b, %ebp + + /* Get the loaded image protocol pointer from the image handle */ + subl $12, %esp // space for the loaded image pointer + pushl %esp // pass its address + leal 4f(%ebp), %eax + pushl %eax // pass the GUID address + pushl 28(%esp) // pass the image handle + + movl 36(%esp), %eax // sys_table + movl ST32_boottime(%eax), %eax // sys_table->boottime + call *BS32_handle_protocol(%eax) // sys_table->boottime->handle_protocol + cmp $0, %eax + jnz 2f + + movl 32(%esp), %ecx // image_handle + movl 36(%esp), %edx // sys_table + movl 12(%esp), %esi // loaded_image + movl LI32_image_base(%esi), %esi // loaded_image->image_base + jmp efi32_pe_stub_entry + +2: addl $24, %esp +3: popl %ebp + ret +SYM_FUNC_END(efi32_pe_entry) + + .section ".rodata" + /* EFI loaded image protocol GUID */ +4: .long 0x5B1B31A1 + .word 0x9562, 0x11d2 + .byte 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B #endif /* diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index cec6baa14d5c..9db98839d7b4 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -424,21 +424,24 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, hdr->ramdisk_image = 0; hdr->ramdisk_size = 0; - status = efi_parse_options(cmdline_ptr); - if (status != EFI_SUCCESS) - goto fail2; - - if (!noinitrd()) { - status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size, - hdr->initrd_addr_max, - above4g ? ULONG_MAX - : hdr->initrd_addr_max); + if (efi_is_native()) { + status = efi_parse_options(cmdline_ptr); if (status != EFI_SUCCESS) goto fail2; - hdr->ramdisk_image = ramdisk_addr & 0xffffffff; - hdr->ramdisk_size = ramdisk_size & 0xffffffff; - boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32; - boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32; + + if (!noinitrd()) { + status = efi_load_initrd(image, &ramdisk_addr, + &ramdisk_size, + hdr->initrd_addr_max, + above4g ? ULONG_MAX + : hdr->initrd_addr_max); + if (status != EFI_SUCCESS) + goto fail2; + hdr->ramdisk_image = ramdisk_addr & 0xffffffff; + hdr->ramdisk_size = ramdisk_size & 0xffffffff; + boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32; + boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32; + } } efi_stub_entry(handle, sys_table, boot_params); From 97aa276579b28b86f4a3e235b50762c0191c2ac3 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 12 Feb 2020 12:18:17 +0100 Subject: [PATCH 0531/2231] efi/x86: Add true mixed mode entry point into .compat section Currently, mixed mode is closely tied to the EFI handover protocol and relies on intimate knowledge of the bootparams structure, setup header etc, all of which are rather byzantine and entirely specific to x86. Even though no other EFI supported architectures are currently known that could support something like mixed mode, it still makes sense to abstract a bit from this, and make it part of a generic Linux on EFI boot protocol. To that end, add a .compat section to the mixed mode binary, and populate it with the PE machine type and entry point address, allowing firmware implementations to match it to their native machine type, and invoke non-native binaries using a secondary entry point. Signed-off-by: Ard Biesheuvel --- arch/x86/boot/Makefile | 2 +- arch/x86/boot/header.S | 20 ++++++++++++++- arch/x86/boot/tools/build.c | 50 ++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index 012b82fc8617..ef9e1f2c836c 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile @@ -88,7 +88,7 @@ $(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE SETUP_OBJS = $(addprefix $(obj)/,$(setup-y)) -sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|z_.*\)$$/\#define ZO_\2 0x\1/p' +sed-zoffset := -e 's/^\([0-9a-fA-F]*\) [a-zA-Z] \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|efi32_pe_entry\|input_data\|kernel_info\|_end\|_ehead\|_text\|z_.*\)$$/\#define ZO_\2 0x\1/p' quiet_cmd_zoffset = ZOFFSET $@ cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@ diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index d59f6604bb42..44aeb63ca6ae 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -106,7 +106,7 @@ coff_header: #else .word 0x8664 # x86-64 #endif - .word 3 # nr_sections + .word section_count # nr_sections .long 0 # TimeDateStamp .long 0 # PointerToSymbolTable .long 1 # NumberOfSymbols @@ -230,6 +230,23 @@ section_table: .word 0 # NumberOfLineNumbers .long 0x42100040 # Characteristics (section flags) +#ifdef CONFIG_EFI_MIXED + # + # The offset & size fields are filled in by build.c. + # + .asciz ".compat" + .long 0 + .long 0x0 + .long 0 # Size of initialized data + # on disk + .long 0x0 + .long 0 # PointerToRelocations + .long 0 # PointerToLineNumbers + .word 0 # NumberOfRelocations + .word 0 # NumberOfLineNumbers + .long 0x42100040 # Characteristics (section flags) +#endif + # # The offset & size fields are filled in by build.c. # @@ -248,6 +265,7 @@ section_table: .word 0 # NumberOfLineNumbers .long 0x60500020 # Characteristics (section flags) + .set section_count, (. - section_table) / 40 #endif /* CONFIG_EFI_STUB */ # Kernel attributes; used by setup. This is part 1 of the diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c index f9f5761c5d05..90d403dfec80 100644 --- a/arch/x86/boot/tools/build.c +++ b/arch/x86/boot/tools/build.c @@ -53,9 +53,16 @@ u8 buf[SETUP_SECT_MAX*512]; #define PECOFF_RELOC_RESERVE 0x20 +#ifdef CONFIG_EFI_MIXED +#define PECOFF_COMPAT_RESERVE 0x20 +#else +#define PECOFF_COMPAT_RESERVE 0x0 +#endif + unsigned long efi32_stub_entry; unsigned long efi64_stub_entry; unsigned long efi_pe_entry; +unsigned long efi32_pe_entry; unsigned long kernel_info; unsigned long startup_64; @@ -189,7 +196,10 @@ static void update_pecoff_section_header(char *section_name, u32 offset, u32 siz static void update_pecoff_setup_and_reloc(unsigned int size) { u32 setup_offset = 0x200; - u32 reloc_offset = size - PECOFF_RELOC_RESERVE; + u32 reloc_offset = size - PECOFF_RELOC_RESERVE - PECOFF_COMPAT_RESERVE; +#ifdef CONFIG_EFI_MIXED + u32 compat_offset = reloc_offset + PECOFF_RELOC_RESERVE; +#endif u32 setup_size = reloc_offset - setup_offset; update_pecoff_section_header(".setup", setup_offset, setup_size); @@ -201,6 +211,20 @@ static void update_pecoff_setup_and_reloc(unsigned int size) */ put_unaligned_le32(reloc_offset + 10, &buf[reloc_offset]); put_unaligned_le32(10, &buf[reloc_offset + 4]); + +#ifdef CONFIG_EFI_MIXED + update_pecoff_section_header(".compat", compat_offset, PECOFF_COMPAT_RESERVE); + + /* + * Put the IA-32 machine type (0x14c) and the associated entry point + * address in the .compat section, so loaders can figure out which other + * execution modes this image supports. + */ + buf[compat_offset] = 0x1; + buf[compat_offset + 1] = 0x8; + put_unaligned_le16(0x14c, &buf[compat_offset + 2]); + put_unaligned_le32(efi32_pe_entry + size, &buf[compat_offset + 4]); +#endif } static void update_pecoff_text(unsigned int text_start, unsigned int file_sz, @@ -212,6 +236,22 @@ static void update_pecoff_text(unsigned int text_start, unsigned int file_sz, pe_header = get_unaligned_le32(&buf[0x3c]); +#ifdef CONFIG_EFI_MIXED + /* + * In mixed mode, we will execute startup_32() at whichever offset in + * memory it happened to land when the PE/COFF loader loaded the image, + * which may be misaligned with respect to the kernel_alignment field + * in the setup header. + * + * In order for startup_32 to safely execute in place at this offset, + * we need to ensure that the CONFIG_PHYSICAL_ALIGN aligned allocation + * it creates for the page tables does not extend beyond the declared + * size of the image in the PE/COFF header. So add the required slack. + */ + bss_sz += CONFIG_PHYSICAL_ALIGN; + init_sz += CONFIG_PHYSICAL_ALIGN; +#endif + /* * Size of code: Subtract the size of the first sector (512 bytes) * which includes the header. @@ -279,6 +319,12 @@ static inline int reserve_pecoff_reloc_section(int c) } #endif /* CONFIG_EFI_STUB */ +static int reserve_pecoff_compat_section(int c) +{ + /* Reserve 0x20 bytes for .compat section */ + memset(buf+c, 0, PECOFF_COMPAT_RESERVE); + return PECOFF_COMPAT_RESERVE; +} /* * Parse zoffset.h and find the entry points. We could just #include zoffset.h @@ -311,6 +357,7 @@ static void parse_zoffset(char *fname) PARSE_ZOFS(p, efi32_stub_entry); PARSE_ZOFS(p, efi64_stub_entry); PARSE_ZOFS(p, efi_pe_entry); + PARSE_ZOFS(p, efi32_pe_entry); PARSE_ZOFS(p, kernel_info); PARSE_ZOFS(p, startup_64); @@ -354,6 +401,7 @@ int main(int argc, char ** argv) die("Boot block hasn't got boot flag (0xAA55)"); fclose(file); + c += reserve_pecoff_compat_section(c); c += reserve_pecoff_reloc_section(c); /* Pad unused space with zeros */ From ac5abc700a43b3d448c86932196db22af470eca1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Feb 2020 09:54:05 +0100 Subject: [PATCH 0532/2231] efi/arm: Move FDT param discovery code out of efi.c On ARM systems, we discover the UEFI system table address and memory map address from the /chosen node in the device tree, or in the Xen case, from a similar node under /hypervisor. Before making some functional changes to that code, move it into its own file that only gets built if CONFIG_EFI_PARAMS_FROM_FDT=y. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/efi.c | 135 ----------------------------- drivers/firmware/efi/fdtparams.c | 142 +++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 135 deletions(-) create mode 100644 drivers/firmware/efi/fdtparams.c diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 554d795270d9..3c5a9690de04 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -13,6 +13,7 @@ KASAN_SANITIZE_runtime-wrappers.o := n obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o tpm.o obj-$(CONFIG_EFI) += capsule.o memmap.o +obj-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdtparams.o obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_ESRT) += esrt.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 718dddfa0a0b..7dbe1487b111 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -646,140 +645,6 @@ static int __init efi_load_efivars(void) device_initcall(efi_load_efivars); #endif -#ifdef CONFIG_EFI_PARAMS_FROM_FDT - -#define UEFI_PARAM(name, prop, field) \ - { \ - { name }, \ - { prop }, \ - offsetof(struct efi_fdt_params, field), \ - sizeof_field(struct efi_fdt_params, field) \ - } - -struct params { - const char name[32]; - const char propname[32]; - int offset; - int size; -}; - -static __initdata struct params fdt_params[] = { - UEFI_PARAM("System Table", "linux,uefi-system-table", system_table), - UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap), - UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size), - UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size), - UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver) -}; - -static __initdata struct params xen_fdt_params[] = { - UEFI_PARAM("System Table", "xen,uefi-system-table", system_table), - UEFI_PARAM("MemMap Address", "xen,uefi-mmap-start", mmap), - UEFI_PARAM("MemMap Size", "xen,uefi-mmap-size", mmap_size), - UEFI_PARAM("MemMap Desc. Size", "xen,uefi-mmap-desc-size", desc_size), - UEFI_PARAM("MemMap Desc. Version", "xen,uefi-mmap-desc-ver", desc_ver) -}; - -#define EFI_FDT_PARAMS_SIZE ARRAY_SIZE(fdt_params) - -static __initdata struct { - const char *uname; - const char *subnode; - struct params *params; -} dt_params[] = { - { "hypervisor", "uefi", xen_fdt_params }, - { "chosen", NULL, fdt_params }, -}; - -struct param_info { - int found; - void *params; - const char *missing; -}; - -static int __init __find_uefi_params(unsigned long node, - struct param_info *info, - struct params *params) -{ - const void *prop; - void *dest; - u64 val; - int i, len; - - for (i = 0; i < EFI_FDT_PARAMS_SIZE; i++) { - prop = of_get_flat_dt_prop(node, params[i].propname, &len); - if (!prop) { - info->missing = params[i].name; - return 0; - } - - dest = info->params + params[i].offset; - info->found++; - - val = of_read_number(prop, len / sizeof(u32)); - - if (params[i].size == sizeof(u32)) - *(u32 *)dest = val; - else - *(u64 *)dest = val; - - if (efi_enabled(EFI_DBG)) - pr_info(" %s: 0x%0*llx\n", params[i].name, - params[i].size * 2, val); - } - - return 1; -} - -static int __init fdt_find_uefi_params(unsigned long node, const char *uname, - int depth, void *data) -{ - struct param_info *info = data; - int i; - - for (i = 0; i < ARRAY_SIZE(dt_params); i++) { - const char *subnode = dt_params[i].subnode; - - if (depth != 1 || strcmp(uname, dt_params[i].uname) != 0) { - info->missing = dt_params[i].params[0].name; - continue; - } - - if (subnode) { - int err = of_get_flat_dt_subnode_by_name(node, subnode); - - if (err < 0) - return 0; - - node = err; - } - - return __find_uefi_params(node, info, dt_params[i].params); - } - - return 0; -} - -int __init efi_get_fdt_params(struct efi_fdt_params *params) -{ - struct param_info info; - int ret; - - pr_info("Getting EFI parameters from FDT:\n"); - - info.found = 0; - info.params = params; - - ret = of_scan_flat_dt(fdt_find_uefi_params, &info); - if (!info.found) - pr_info("UEFI not found.\n"); - else if (!ret) - pr_err("Can't find '%s' in device tree!\n", - info.missing); - - return ret; -} -#endif /* CONFIG_EFI_PARAMS_FROM_FDT */ - static __initdata char memory_type_name[][20] = { "Reserved", "Loader Code", diff --git a/drivers/firmware/efi/fdtparams.c b/drivers/firmware/efi/fdtparams.c new file mode 100644 index 000000000000..3de343faffc6 --- /dev/null +++ b/drivers/firmware/efi/fdtparams.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) "efi: " fmt + +#include +#include +#include +#include +#include + +#include + +#define UEFI_PARAM(name, prop, field) \ + { \ + { name }, \ + { prop }, \ + offsetof(struct efi_fdt_params, field), \ + sizeof_field(struct efi_fdt_params, field) \ + } + +struct params { + const char name[32]; + const char propname[32]; + int offset; + int size; +}; + +static __initdata struct params fdt_params[] = { + UEFI_PARAM("System Table", "linux,uefi-system-table", system_table), + UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap), + UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size), + UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size), + UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver) +}; + +static __initdata struct params xen_fdt_params[] = { + UEFI_PARAM("System Table", "xen,uefi-system-table", system_table), + UEFI_PARAM("MemMap Address", "xen,uefi-mmap-start", mmap), + UEFI_PARAM("MemMap Size", "xen,uefi-mmap-size", mmap_size), + UEFI_PARAM("MemMap Desc. Size", "xen,uefi-mmap-desc-size", desc_size), + UEFI_PARAM("MemMap Desc. Version", "xen,uefi-mmap-desc-ver", desc_ver) +}; + +#define EFI_FDT_PARAMS_SIZE ARRAY_SIZE(fdt_params) + +static __initdata struct { + const char *uname; + const char *subnode; + struct params *params; +} dt_params[] = { + { "hypervisor", "uefi", xen_fdt_params }, + { "chosen", NULL, fdt_params }, +}; + +struct param_info { + int found; + void *params; + const char *missing; +}; + +static int __init __find_uefi_params(unsigned long node, + struct param_info *info, + struct params *params) +{ + const void *prop; + void *dest; + u64 val; + int i, len; + + for (i = 0; i < EFI_FDT_PARAMS_SIZE; i++) { + prop = of_get_flat_dt_prop(node, params[i].propname, &len); + if (!prop) { + info->missing = params[i].name; + return 0; + } + + dest = info->params + params[i].offset; + info->found++; + + val = of_read_number(prop, len / sizeof(u32)); + + if (params[i].size == sizeof(u32)) + *(u32 *)dest = val; + else + *(u64 *)dest = val; + + if (efi_enabled(EFI_DBG)) + pr_info(" %s: 0x%0*llx\n", params[i].name, + params[i].size * 2, val); + } + + return 1; +} + +static int __init fdt_find_uefi_params(unsigned long node, const char *uname, + int depth, void *data) +{ + struct param_info *info = data; + int i; + + for (i = 0; i < ARRAY_SIZE(dt_params); i++) { + const char *subnode = dt_params[i].subnode; + + if (depth != 1 || strcmp(uname, dt_params[i].uname) != 0) { + info->missing = dt_params[i].params[0].name; + continue; + } + + if (subnode) { + int err = of_get_flat_dt_subnode_by_name(node, subnode); + + if (err < 0) + return 0; + + node = err; + } + + return __find_uefi_params(node, info, dt_params[i].params); + } + + return 0; +} + +int __init efi_get_fdt_params(struct efi_fdt_params *params) +{ + struct param_info info; + int ret; + + pr_info("Getting EFI parameters from FDT:\n"); + + info.found = 0; + info.params = params; + + ret = of_scan_flat_dt(fdt_find_uefi_params, &info); + if (!info.found) + pr_info("UEFI not found.\n"); + else if (!ret) + pr_err("Can't find '%s' in device tree!\n", + info.missing); + + return ret; +} From 3b2e4b4c634cc7dd4730ce3e1c75b8206dcc4b04 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Feb 2020 10:19:34 +0100 Subject: [PATCH 0533/2231] efi/arm: Move FDT specific definitions into fdtparams.c Push the FDT params specific types and definition into fdtparams.c, and instead, pass a reference to the memory map data structure and populate it directly, and return the system table address as the return value. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/arm-init.c | 17 ++++++----------- drivers/firmware/efi/fdtparams.c | 30 +++++++++++++++++++++++------- include/linux/efi.h | 10 +--------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 76bf5b22e49e..2791a8048f30 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -205,17 +205,13 @@ static __init void reserve_regions(void) void __init efi_init(void) { struct efi_memory_map_data data; - struct efi_fdt_params params; + u64 efi_system_table; /* Grab UEFI information placed in FDT by stub */ - if (!efi_get_fdt_params(¶ms)) + efi_system_table = efi_get_fdt_params(&data); + if (!efi_system_table) return; - data.desc_version = params.desc_ver; - data.desc_size = params.desc_size; - data.size = params.mmap_size; - data.phys_map = params.mmap; - if (efi_memmap_init_early(&data) < 0) { /* * If we are booting via UEFI, the UEFI memory map is the only @@ -229,7 +225,7 @@ void __init efi_init(void) "Unexpected EFI_MEMORY_DESCRIPTOR version %ld", efi.memmap.desc_version); - if (uefi_init(params.system_table) < 0) { + if (uefi_init(efi_system_table) < 0) { efi_memmap_unmap(); return; } @@ -237,9 +233,8 @@ void __init efi_init(void) reserve_regions(); efi_esrt_init(); - memblock_reserve(params.mmap & PAGE_MASK, - PAGE_ALIGN(params.mmap_size + - (params.mmap & ~PAGE_MASK))); + memblock_reserve(data.phys_map & PAGE_MASK, + PAGE_ALIGN(data.size + (data.phys_map & ~PAGE_MASK))); init_screen_info(); diff --git a/drivers/firmware/efi/fdtparams.c b/drivers/firmware/efi/fdtparams.c index 3de343faffc6..7a384b307c56 100644 --- a/drivers/firmware/efi/fdtparams.c +++ b/drivers/firmware/efi/fdtparams.c @@ -18,6 +18,14 @@ sizeof_field(struct efi_fdt_params, field) \ } +struct efi_fdt_params { + u64 system_table; + u64 mmap; + u32 mmap_size; + u32 desc_size; + u32 desc_ver; +}; + struct params { const char name[32]; const char propname[32]; @@ -121,22 +129,30 @@ static int __init fdt_find_uefi_params(unsigned long node, const char *uname, return 0; } -int __init efi_get_fdt_params(struct efi_fdt_params *params) +u64 __init efi_get_fdt_params(struct efi_memory_map_data *memmap) { + struct efi_fdt_params params; struct param_info info; int ret; pr_info("Getting EFI parameters from FDT:\n"); info.found = 0; - info.params = params; + info.params = ¶ms; ret = of_scan_flat_dt(fdt_find_uefi_params, &info); - if (!info.found) + if (!info.found) { pr_info("UEFI not found.\n"); - else if (!ret) - pr_err("Can't find '%s' in device tree!\n", - info.missing); + return 0; + } else if (!ret) { + pr_err("Can't find '%s' in device tree!\n", info.missing); + return 0; + } - return ret; + memmap->desc_version = params.desc_ver; + memmap->desc_size = params.desc_size; + memmap->size = params.mmap_size; + memmap->phys_map = params.mmap; + + return params.system_table; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 575e6aa39514..a0008e3d4e9d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -477,14 +477,6 @@ struct efi_mem_range { u64 attribute; }; -struct efi_fdt_params { - u64 system_table; - u64 mmap; - u32 mmap_size; - u32 desc_size; - u32 desc_ver; -}; - typedef struct { u32 version; u32 length; @@ -631,7 +623,7 @@ extern void efi_mem_reserve(phys_addr_t addr, u64 size); extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); -extern int efi_get_fdt_params(struct efi_fdt_params *params); +extern u64 efi_get_fdt_params(struct efi_memory_map_data *data); extern struct kobject *efi_kobj; extern int efi_reboot_quirk_mode; From e457ed516a67ea2087950960faaf89ebfb606f06 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 19 Feb 2020 15:29:06 +0100 Subject: [PATCH 0534/2231] efi/arm: Rewrite FDT param discovery routines The efi_get_fdt_params() routine uses the early OF device tree traversal helpers, that iterate over each node in the DT and invoke a caller provided callback that can inspect the node's contents and look for the required data. This requires a special param struct to be passed around, with pointers into param enumeration structs that contain (and duplicate) property names and offsets into yet another struct that carries the collected data. Since we know the data we look for is either under /hypervisor/uefi or under /chosen, it is much simpler to use the libfdt routines, and just try to grab a reference to either node directly, and read each property in sequence. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/fdtparams.c | 214 +++++++++++++------------------ 1 file changed, 91 insertions(+), 123 deletions(-) diff --git a/drivers/firmware/efi/fdtparams.c b/drivers/firmware/efi/fdtparams.c index 7a384b307c56..bb042ab7c2be 100644 --- a/drivers/firmware/efi/fdtparams.c +++ b/drivers/firmware/efi/fdtparams.c @@ -5,154 +5,122 @@ #include #include #include -#include +#include #include -#include +#include -#define UEFI_PARAM(name, prop, field) \ - { \ - { name }, \ - { prop }, \ - offsetof(struct efi_fdt_params, field), \ - sizeof_field(struct efi_fdt_params, field) \ - } +enum { + SYSTAB, + MMBASE, + MMSIZE, + DCSIZE, + DCVERS, -struct efi_fdt_params { - u64 system_table; - u64 mmap; - u32 mmap_size; - u32 desc_size; - u32 desc_ver; + PARAMCOUNT }; -struct params { - const char name[32]; - const char propname[32]; - int offset; - int size; +static __initconst const char name[][22] = { + [SYSTAB] = "System Table ", + [MMBASE] = "MemMap Address ", + [MMSIZE] = "MemMap Size ", + [DCSIZE] = "MemMap Desc. Size ", + [DCVERS] = "MemMap Desc. Version ", }; -static __initdata struct params fdt_params[] = { - UEFI_PARAM("System Table", "linux,uefi-system-table", system_table), - UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap), - UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size), - UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size), - UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver) -}; - -static __initdata struct params xen_fdt_params[] = { - UEFI_PARAM("System Table", "xen,uefi-system-table", system_table), - UEFI_PARAM("MemMap Address", "xen,uefi-mmap-start", mmap), - UEFI_PARAM("MemMap Size", "xen,uefi-mmap-size", mmap_size), - UEFI_PARAM("MemMap Desc. Size", "xen,uefi-mmap-desc-size", desc_size), - UEFI_PARAM("MemMap Desc. Version", "xen,uefi-mmap-desc-ver", desc_ver) -}; - -#define EFI_FDT_PARAMS_SIZE ARRAY_SIZE(fdt_params) - -static __initdata struct { - const char *uname; - const char *subnode; - struct params *params; +static __initconst const struct { + const char path[17]; + const char params[PARAMCOUNT][26]; } dt_params[] = { - { "hypervisor", "uefi", xen_fdt_params }, - { "chosen", NULL, fdt_params }, + { +#ifdef CONFIG_XEN // <-------17------> + .path = "/hypervisor/uefi", + .params = { + [SYSTAB] = "xen,uefi-system-table", + [MMBASE] = "xen,uefi-mmap-start", + [MMSIZE] = "xen,uefi-mmap-size", + [DCSIZE] = "xen,uefi-mmap-desc-size", + [DCVERS] = "xen,uefi-mmap-desc-ver", + } + }, { +#endif + .path = "/chosen", + .params = { // <-----------26-----------> + [SYSTAB] = "linux,uefi-system-table", + [MMBASE] = "linux,uefi-mmap-start", + [MMSIZE] = "linux,uefi-mmap-size", + [DCSIZE] = "linux,uefi-mmap-desc-size", + [DCVERS] = "linux,uefi-mmap-desc-ver", + } + } }; -struct param_info { - int found; - void *params; - const char *missing; -}; - -static int __init __find_uefi_params(unsigned long node, - struct param_info *info, - struct params *params) +static int __init efi_get_fdt_prop(const void *fdt, int node, const char *pname, + const char *rname, void *var, int size) { const void *prop; - void *dest; + int len; u64 val; - int i, len; - for (i = 0; i < EFI_FDT_PARAMS_SIZE; i++) { - prop = of_get_flat_dt_prop(node, params[i].propname, &len); - if (!prop) { - info->missing = params[i].name; - return 0; - } + prop = fdt_getprop(fdt, node, pname, &len); + if (!prop) + return 1; - dest = info->params + params[i].offset; - info->found++; + val = (len == 4) ? (u64)be32_to_cpup(prop) : get_unaligned_be64(prop); - val = of_read_number(prop, len / sizeof(u32)); + if (size == 8) + *(u64 *)var = val; + else + *(u32 *)var = (val < U32_MAX) ? val : U32_MAX; // saturate - if (params[i].size == sizeof(u32)) - *(u32 *)dest = val; - else - *(u64 *)dest = val; - - if (efi_enabled(EFI_DBG)) - pr_info(" %s: 0x%0*llx\n", params[i].name, - params[i].size * 2, val); - } - - return 1; -} - -static int __init fdt_find_uefi_params(unsigned long node, const char *uname, - int depth, void *data) -{ - struct param_info *info = data; - int i; - - for (i = 0; i < ARRAY_SIZE(dt_params); i++) { - const char *subnode = dt_params[i].subnode; - - if (depth != 1 || strcmp(uname, dt_params[i].uname) != 0) { - info->missing = dt_params[i].params[0].name; - continue; - } - - if (subnode) { - int err = of_get_flat_dt_subnode_by_name(node, subnode); - - if (err < 0) - return 0; - - node = err; - } - - return __find_uefi_params(node, info, dt_params[i].params); - } + if (efi_enabled(EFI_DBG)) + pr_info(" %s: 0x%0*llx\n", rname, size * 2, val); return 0; } -u64 __init efi_get_fdt_params(struct efi_memory_map_data *memmap) +u64 __init efi_get_fdt_params(struct efi_memory_map_data *mm) { - struct efi_fdt_params params; - struct param_info info; - int ret; + const void *fdt = initial_boot_params; + unsigned long systab; + int i, j, node; + struct { + void *var; + int size; + } target[] = { + [SYSTAB] = { &systab, sizeof(systab) }, + [MMBASE] = { &mm->phys_map, sizeof(mm->phys_map) }, + [MMSIZE] = { &mm->size, sizeof(mm->size) }, + [DCSIZE] = { &mm->desc_size, sizeof(mm->desc_size) }, + [DCVERS] = { &mm->desc_version, sizeof(mm->desc_version) }, + }; - pr_info("Getting EFI parameters from FDT:\n"); + BUILD_BUG_ON(ARRAY_SIZE(target) != ARRAY_SIZE(name)); + BUILD_BUG_ON(ARRAY_SIZE(target) != ARRAY_SIZE(dt_params[0].params)); - info.found = 0; - info.params = ¶ms; + for (i = 0; i < ARRAY_SIZE(dt_params); i++) { + node = fdt_path_offset(fdt, dt_params[i].path); + if (node < 0) + continue; - ret = of_scan_flat_dt(fdt_find_uefi_params, &info); - if (!info.found) { - pr_info("UEFI not found.\n"); - return 0; - } else if (!ret) { - pr_err("Can't find '%s' in device tree!\n", info.missing); - return 0; + if (efi_enabled(EFI_DBG)) + pr_info("Getting UEFI parameters from %s in DT:\n", + dt_params[i].path); + + for (j = 0; j < ARRAY_SIZE(target); j++) { + const char *pname = dt_params[i].params[j]; + + if (!efi_get_fdt_prop(fdt, node, pname, name[j], + target[j].var, target[j].size)) + continue; + if (!j) + goto notfound; + pr_err("Can't find property '%s' in DT!\n", pname); + return 0; + } + return systab; } - - memmap->desc_version = params.desc_ver; - memmap->desc_size = params.desc_size; - memmap->size = params.mmap_size; - memmap->phys_map = params.mmap; - - return params.system_table; +notfound: + pr_info("UEFI not found.\n"); + return 0; } From 96a3dd3dece8134ba19b0ded7e6663136d3107b9 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 21 Jan 2020 11:17:47 +0100 Subject: [PATCH 0535/2231] efi: Store mask of supported runtime services in struct efi Revision 2.8 of the UEFI spec introduces provisions for firmware to advertise lack of support for certain runtime services at OS runtime. Let's store this mask in struct efi for easy access. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 27 +++++++++++++++++---------- include/linux/efi.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 7dbe1487b111..703a019d81b4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -34,6 +34,7 @@ #include struct efi __read_mostly efi = { + .runtime_supported_mask = EFI_RT_SUPPORTED_ALL, .acpi = EFI_INVALID_TABLE_ADDR, .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, @@ -301,16 +302,22 @@ static int __init efisubsys_init(void) if (!efi_enabled(EFI_BOOT)) return 0; - /* - * Since we process only one efi_runtime_service() at a time, an - * ordered workqueue (which creates only one execution context) - * should suffice all our needs. - */ - efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0); - if (!efi_rts_wq) { - pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n"); - clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); - return 0; + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + efi.runtime_supported_mask = 0; + + if (efi.runtime_supported_mask) { + /* + * Since we process only one efi_runtime_service() at a time, an + * ordered workqueue (which creates only one execution context) + * should suffice for all our needs. + */ + efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0); + if (!efi_rts_wq) { + pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n"); + clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); + efi.runtime_supported_mask = 0; + return 0; + } } /* We register the efi directory at /sys/firmware/efi */ diff --git a/include/linux/efi.h b/include/linux/efi.h index a0008e3d4e9d..57695f400044 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -523,6 +523,7 @@ typedef struct { extern struct efi { const efi_runtime_services_t *runtime; /* EFI runtime services table */ unsigned int runtime_version; /* Runtime services version */ + unsigned int runtime_supported_mask; unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ unsigned long acpi20; /* ACPI table (ACPI 2.0) */ @@ -551,6 +552,26 @@ extern struct efi { unsigned long flags; } efi; +#define EFI_RT_SUPPORTED_GET_TIME 0x0001 +#define EFI_RT_SUPPORTED_SET_TIME 0x0002 +#define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 +#define EFI_RT_SUPPORTED_SET_WAKEUP_TIME 0x0008 +#define EFI_RT_SUPPORTED_GET_VARIABLE 0x0010 +#define EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME 0x0020 +#define EFI_RT_SUPPORTED_SET_VARIABLE 0x0040 +#define EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP 0x0080 +#define EFI_RT_SUPPORTED_CONVERT_POINTER 0x0100 +#define EFI_RT_SUPPORTED_GET_NEXT_HIGH_MONOTONIC_COUNT 0x0200 +#define EFI_RT_SUPPORTED_RESET_SYSTEM 0x0400 +#define EFI_RT_SUPPORTED_UPDATE_CAPSULE 0x0800 +#define EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES 0x1000 +#define EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO 0x2000 + +#define EFI_RT_SUPPORTED_ALL 0x3fff + +#define EFI_RT_SUPPORTED_TIME_SERVICES 0x000f +#define EFI_RT_SUPPORTED_VARIABLE_SERVICES 0x0070 + extern struct mm_struct efi_mm; static inline int @@ -761,6 +782,11 @@ static inline bool __pure efi_soft_reserve_enabled(void) return IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && __efi_soft_reserve_enabled(); } + +static inline bool efi_rt_services_supported(unsigned int mask) +{ + return (efi.runtime_supported_mask & mask) == mask; +} #else static inline bool efi_enabled(int feature) { @@ -779,6 +805,11 @@ static inline bool efi_soft_reserve_enabled(void) { return false; } + +static inline bool efi_rt_services_supported(unsigned int mask) +{ + return false; +} #endif extern int efi_status_to_err(efi_status_t status); From fe4db90a80cd12ebe4efe385d40d6636330149ed Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Jan 2020 13:10:25 +0100 Subject: [PATCH 0536/2231] efi: Add support for EFI_RT_PROPERTIES table Take the newly introduced EFI_RT_PROPERTIES_TABLE configuration table into account, which carries a mask of which EFI runtime services are still functional after ExitBootServices() has been called by the OS. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 12 ++++++++++++ include/linux/efi.h | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 703a019d81b4..a35230517f9c 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -47,6 +47,7 @@ EXPORT_SYMBOL(efi); static unsigned long __ro_after_init rng_seed = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; +static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; struct mm_struct efi_mm = { .mm_rb = RB_ROOT, @@ -449,6 +450,7 @@ static const efi_config_table_type_t common_tables[] __initconst = { {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve}, + {EFI_RT_PROPERTIES_TABLE_GUID, "RTPROP", &rt_prop}, #ifdef CONFIG_EFI_RCI2_TABLE {DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys}, #endif @@ -575,6 +577,16 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, } } + if (rt_prop != EFI_INVALID_TABLE_ADDR) { + efi_rt_properties_table_t *tbl; + + tbl = early_memremap(rt_prop, sizeof(*tbl)); + if (tbl) { + efi.runtime_supported_mask &= tbl->runtime_services_supported; + early_memunmap(tbl, sizeof(*tbl)); + } + } + return 0; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 57695f400044..2ab33d5d6ca5 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -334,6 +334,7 @@ void efi_native_runtime_setup(void); #define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f) #define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) #define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d) +#define EFI_RT_PROPERTIES_TABLE_GUID EFI_GUID(0xeb66918a, 0x7eef, 0x402a, 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9) #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) @@ -486,6 +487,14 @@ typedef struct { #define EFI_PROPERTIES_TABLE_VERSION 0x00010000 #define EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA 0x1 +typedef struct { + u16 version; + u16 length; + u32 runtime_services_supported; +} efi_rt_properties_table_t; + +#define EFI_RT_PROPERTIES_TABLE_VERSION 0x1 + #define EFI_INVALID_TABLE_ADDR (~0UL) typedef struct { From bf67fad19e493bd3f7267b35b97b860280f87acc Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Jan 2020 09:12:00 +0100 Subject: [PATCH 0537/2231] efi: Use more granular check for availability for variable services The UEFI spec rev 2.8 permits firmware implementations to support only a subset of EFI runtime services at OS runtime (i.e., after the call to ExitBootServices()), so let's take this into account in the drivers that rely specifically on the availability of the EFI variable services. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi-pstore.c | 2 +- drivers/firmware/efi/efi.c | 28 ++++++++-------------------- drivers/firmware/efi/efivars.c | 2 +- fs/efivarfs/super.c | 2 +- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 9ea13e8d12ec..d2f6855d205b 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -356,7 +356,7 @@ static struct pstore_info efi_pstore_info = { static __init int efivars_pstore_init(void) { - if (!efi_enabled(EFI_RUNTIME_SERVICES)) + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES)) return 0; if (!efivars_kobject()) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index a35230517f9c..abf4c02e0201 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -328,12 +328,13 @@ static int __init efisubsys_init(void) return -ENOMEM; } - error = generic_ops_register(); - if (error) - goto err_put; - - if (efi_enabled(EFI_RUNTIME_SERVICES)) + if (efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES)) { efivar_ssdt_load(); + error = generic_ops_register(); + if (error) + goto err_put; + platform_device_register_simple("efivars", 0, NULL, 0); + } error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); if (error) { @@ -358,7 +359,8 @@ static int __init efisubsys_init(void) err_remove_group: sysfs_remove_group(efi_kobj, &efi_subsys_attr_group); err_unregister: - generic_ops_unregister(); + if (efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES)) + generic_ops_unregister(); err_put: kobject_put(efi_kobj); return error; @@ -650,20 +652,6 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, vendor); } -#ifdef CONFIG_EFI_VARS_MODULE -static int __init efi_load_efivars(void) -{ - struct platform_device *pdev; - - if (!efi_enabled(EFI_RUNTIME_SERVICES)) - return 0; - - pdev = platform_device_register_simple("efivars", 0, NULL, 0); - return PTR_ERR_OR_ZERO(pdev); -} -device_initcall(efi_load_efivars); -#endif - static __initdata char memory_type_name[][20] = { "Reserved", "Loader Code", diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 7576450c8254..d309abca5091 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -664,7 +664,7 @@ int efivars_sysfs_init(void) struct kobject *parent_kobj = efivars_kobject(); int error = 0; - if (!efi_enabled(EFI_RUNTIME_SERVICES)) + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES)) return -ENODEV; /* No efivars has been registered yet */ diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index fa4f6447ddad..12c66f5d92dd 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -252,7 +252,7 @@ static struct file_system_type efivarfs_type = { static __init int efivarfs_init(void) { - if (!efi_enabled(EFI_RUNTIME_SERVICES)) + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES)) return -ENODEV; if (!efivars_kobject()) From e5c3b1cc99451d97ed3b94aff09817c66c040399 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Jan 2020 09:14:09 +0100 Subject: [PATCH 0538/2231] efi: Register EFI rtc platform device only when available Drop the separate driver that registers the EFI rtc on all EFI systems that have runtime services available, and instead, move the registration into the core EFI code, and make it conditional on whether the actual time related services are available. Acked-by: Alexandre Belloni Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 3 +++ drivers/rtc/Makefile | 4 ---- drivers/rtc/rtc-efi-platform.c | 35 ---------------------------------- 3 files changed, 3 insertions(+), 39 deletions(-) delete mode 100644 drivers/rtc/rtc-efi-platform.c diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index abf4c02e0201..69a585106d30 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -321,6 +321,9 @@ static int __init efisubsys_init(void) } } + if (efi_rt_services_supported(EFI_RT_SUPPORTED_TIME_SERVICES)) + platform_device_register_simple("rtc-efi", 0, NULL, 0); + /* We register the efi directory at /sys/firmware/efi */ efi_kobj = kobject_create_and_add("efi", firmware_kobj); if (!efi_kobj) { diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 4ac8f19fb631..24c7dfa1bd7d 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -12,10 +12,6 @@ obj-$(CONFIG_RTC_CLASS) += rtc-core.o obj-$(CONFIG_RTC_MC146818_LIB) += rtc-mc146818-lib.o rtc-core-y := class.o interface.o -ifdef CONFIG_RTC_DRV_EFI -rtc-core-y += rtc-efi-platform.o -endif - rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o diff --git a/drivers/rtc/rtc-efi-platform.c b/drivers/rtc/rtc-efi-platform.c deleted file mode 100644 index 6c037dc4e3dc..000000000000 --- a/drivers/rtc/rtc-efi-platform.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Moved from arch/ia64/kernel/time.c - * - * Copyright (C) 1998-2003 Hewlett-Packard Co - * Stephane Eranian - * David Mosberger - * Copyright (C) 1999 Don Dugger - * Copyright (C) 1999-2000 VA Linux Systems - * Copyright (C) 1999-2000 Walt Drummond - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include - -static struct platform_device rtc_efi_dev = { - .name = "rtc-efi", - .id = -1, -}; - -static int __init rtc_init(void) -{ - if (efi_enabled(EFI_RUNTIME_SERVICES)) - if (platform_device_register(&rtc_efi_dev) < 0) - pr_err("unable to register rtc device...\n"); - - /* not necessarily an error */ - return 0; -} -module_init(rtc_init); From d79b348c3540611552fc5fb87565228925fb1fd8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Jan 2020 09:16:02 +0100 Subject: [PATCH 0539/2231] infiniband: hfi1: Use EFI GetVariable only when available Replace the EFI runtime services check with one that tells us whether EFI GetVariable() is implemented by the firmware. Cc: Mike Marciniszyn Cc: Dennis Dalessandro Cc: Doug Ledford Cc: Jason Gunthorpe Cc: linux-rdma@vger.kernel.org Signed-off-by: Ard Biesheuvel --- drivers/infiniband/hw/hfi1/efivar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/hfi1/efivar.c b/drivers/infiniband/hw/hfi1/efivar.c index d106d23016ba..c22ab7b5163b 100644 --- a/drivers/infiniband/hw/hfi1/efivar.c +++ b/drivers/infiniband/hw/hfi1/efivar.c @@ -78,7 +78,7 @@ static int read_efi_var(const char *name, unsigned long *size, *size = 0; *return_data = NULL; - if (!efi_enabled(EFI_RUNTIME_SERVICES)) + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) return -EOPNOTSUPP; uni_name = kcalloc(strlen(name) + 1, sizeof(efi_char16_t), GFP_KERNEL); From 69f4cab134d204f0260d69ae407a2562e56a5025 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Jan 2020 09:17:48 +0100 Subject: [PATCH 0540/2231] scsi: iscsi: Use EFI GetVariable only when available Replace the EFI runtime services check with one that tells us whether EFI GetVariable() is implemented by the firmware. Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: linux-scsi@vger.kernel.org Signed-off-by: Ard Biesheuvel --- drivers/scsi/isci/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index b48aac8dfcb8..974c3b9116d5 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -621,7 +621,7 @@ static int isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; pci_set_drvdata(pdev, pci_info); - if (efi_enabled(EFI_RUNTIME_SERVICES)) + if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) orom = isci_get_efi_var(pdev); if (!orom) From 9b42f76ad58b3f641197d031766c6979acf4b4a6 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Jan 2020 13:12:14 +0100 Subject: [PATCH 0541/2231] efi: Use EFI ResetSystem only when available Do not attempt to call EFI ResetSystem if the runtime supported mask tells us it is no longer functional at OS runtime. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/reboot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/reboot.c b/drivers/firmware/efi/reboot.c index 7effff969eb9..73089a24f04b 100644 --- a/drivers/firmware/efi/reboot.c +++ b/drivers/firmware/efi/reboot.c @@ -15,7 +15,7 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) const char *str[] = { "cold", "warm", "shutdown", "platform" }; int efi_mode, cap_reset_mode; - if (!efi_enabled(EFI_RUNTIME_SERVICES)) + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_RESET_SYSTEM)) return; switch (reboot_mode) { @@ -64,7 +64,7 @@ static void efi_power_off(void) static int __init efi_shutdown_init(void) { - if (!efi_enabled(EFI_RUNTIME_SERVICES)) + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_RESET_SYSTEM)) return -ENODEV; if (efi_poweroff_required()) { From 9a440391b560347bf5ee7cb96b63e7e91cedf66a Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Jan 2020 13:09:35 +0100 Subject: [PATCH 0542/2231] x86/ima: Use EFI GetVariable only when available Replace the EFI runtime services check with one that tells us whether EFI GetVariable() is implemented by the firmware. Signed-off-by: Ard Biesheuvel --- arch/x86/kernel/ima_arch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/ima_arch.c b/arch/x86/kernel/ima_arch.c index 4d4f5d9faac3..cb6ed616a543 100644 --- a/arch/x86/kernel/ima_arch.c +++ b/arch/x86/kernel/ima_arch.c @@ -19,7 +19,7 @@ static enum efi_secureboot_mode get_sb_mode(void) size = sizeof(secboot); - if (!efi_enabled(EFI_RUNTIME_SERVICES)) { + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) { pr_info("ima: secureboot mode unknown, no efi\n"); return efi_secureboot_mode_unknown; } From 6b75d54d5258ccd655387a00bbe1b00f92f4d965 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 16 Feb 2020 19:46:25 +0100 Subject: [PATCH 0543/2231] integrity: Check properly whether EFI GetVariable() is available Testing the value of the efi.get_variable function pointer is not the right way to establish whether the platform supports EFI variables at runtime. Instead, use the newly added granular check that can test for the presence of each EFI runtime service individually. Acked-by: Serge Hallyn Signed-off-by: Ard Biesheuvel --- security/integrity/platform_certs/load_uefi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c index 111898aad56e..e2fe1bd3abb9 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -76,7 +76,7 @@ static int __init load_uefi_certs(void) unsigned long dbsize = 0, dbxsize = 0, moksize = 0; int rc = 0; - if (!efi.get_variable) + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) return false; /* Get db, MokListRT, and dbx. They might not exist, so it isn't From a3326a0d878c433d69981c504f8c8ade60cd2b51 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Feb 2020 10:56:59 +0100 Subject: [PATCH 0544/2231] efi/x86: Use symbolic constants in PE header instead of bare numbers Replace bare numbers in the PE/COFF header structure with symbolic constants so they become self documenting. Signed-off-by: Ard Biesheuvel --- arch/x86/boot/header.S | 62 ++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index 44aeb63ca6ae..f65661a58cf8 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -15,7 +15,7 @@ * hex while segment addresses are written as segment:offset. * */ - +#include #include #include #include @@ -43,8 +43,7 @@ SYSSEG = 0x1000 /* historical load address >> 4 */ bootsect_start: #ifdef CONFIG_EFI_STUB # "MZ", MS-DOS header - .byte 0x4d - .byte 0x5a + .word MZ_MAGIC #endif # Normalize the start address @@ -97,39 +96,30 @@ bugger_off_msg: #ifdef CONFIG_EFI_STUB pe_header: - .ascii "PE" - .word 0 + .long PE_MAGIC coff_header: #ifdef CONFIG_X86_32 - .word 0x14c # i386 + .set image_file_add_flags, IMAGE_FILE_32BIT_MACHINE + .set pe_opt_magic, PE_OPT_MAGIC_PE32 + .word IMAGE_FILE_MACHINE_I386 #else - .word 0x8664 # x86-64 + .set image_file_add_flags, 0 + .set pe_opt_magic, PE_OPT_MAGIC_PE32PLUS + .word IMAGE_FILE_MACHINE_AMD64 #endif .word section_count # nr_sections .long 0 # TimeDateStamp .long 0 # PointerToSymbolTable .long 1 # NumberOfSymbols .word section_table - optional_header # SizeOfOptionalHeader -#ifdef CONFIG_X86_32 - .word 0x306 # Characteristics. - # IMAGE_FILE_32BIT_MACHINE | - # IMAGE_FILE_DEBUG_STRIPPED | - # IMAGE_FILE_EXECUTABLE_IMAGE | - # IMAGE_FILE_LINE_NUMS_STRIPPED -#else - .word 0x206 # Characteristics - # IMAGE_FILE_DEBUG_STRIPPED | - # IMAGE_FILE_EXECUTABLE_IMAGE | - # IMAGE_FILE_LINE_NUMS_STRIPPED -#endif + .word IMAGE_FILE_EXECUTABLE_IMAGE | \ + image_file_add_flags | \ + IMAGE_FILE_DEBUG_STRIPPED | \ + IMAGE_FILE_LINE_NUMS_STRIPPED # Characteristics optional_header: -#ifdef CONFIG_X86_32 - .word 0x10b # PE32 format -#else - .word 0x20b # PE32+ format -#endif + .word pe_opt_magic .byte 0x02 # MajorLinkerVersion .byte 0x14 # MinorLinkerVersion @@ -170,7 +160,7 @@ extra_header_fields: .long 0x200 # SizeOfHeaders .long 0 # CheckSum - .word 0xa # Subsystem (EFI application) + .word IMAGE_SUBSYSTEM_EFI_APPLICATION # Subsystem (EFI application) .word 0 # DllCharacteristics #ifdef CONFIG_X86_32 .long 0 # SizeOfStackReserve @@ -184,7 +174,7 @@ extra_header_fields: .quad 0 # SizeOfHeapCommit #endif .long 0 # LoaderFlags - .long 0x6 # NumberOfRvaAndSizes + .long (section_table - .) / 8 # NumberOfRvaAndSizes .quad 0 # ExportTable .quad 0 # ImportTable @@ -210,7 +200,10 @@ section_table: .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers - .long 0x60500020 # Characteristics (section flags) + .long IMAGE_SCN_CNT_CODE | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_EXECUTE | \ + IMAGE_SCN_ALIGN_16BYTES # Characteristics # # The EFI application loader requires a relocation section @@ -228,7 +221,10 @@ section_table: .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers - .long 0x42100040 # Characteristics (section flags) + .long IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_DISCARDABLE | \ + IMAGE_SCN_ALIGN_1BYTES # Characteristics #ifdef CONFIG_EFI_MIXED # @@ -244,7 +240,10 @@ section_table: .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers - .long 0x42100040 # Characteristics (section flags) + .long IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_DISCARDABLE | \ + IMAGE_SCN_ALIGN_1BYTES # Characteristics #endif # @@ -263,7 +262,10 @@ section_table: .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations .word 0 # NumberOfLineNumbers - .long 0x60500020 # Characteristics (section flags) + .long IMAGE_SCN_CNT_CODE | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_EXECUTE | \ + IMAGE_SCN_ALIGN_16BYTES # Characteristics .set section_count, (. - section_table) / 40 #endif /* CONFIG_EFI_STUB */ From 148d3f716c208738d2c7ff8c80be18d89ec8a06e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Feb 2020 11:06:00 +0100 Subject: [PATCH 0545/2231] efi/libstub: Introduce symbolic constants for the stub major/minor version Now that we have added new ways to load the initrd or the mixed mode kernel, we will also need a way to tell the loader about this. Add symbolic constants for the PE/COFF major/minor version numbers (which fortunately have always been 0x0 for all architectures), so that we can bump them later to document the capabilities of the stub. Signed-off-by: Ard Biesheuvel --- arch/arm/boot/compressed/efi-header.S | 4 ++-- arch/arm64/kernel/efi-header.S | 4 ++-- arch/x86/boot/header.S | 4 ++-- include/linux/pe.h | 3 +++ 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S index e38fbda02b93..62286da318e7 100644 --- a/arch/arm/boot/compressed/efi-header.S +++ b/arch/arm/boot/compressed/efi-header.S @@ -70,8 +70,8 @@ extra_header_fields: .long SZ_512 @ FileAlignment .short 0 @ MajorOsVersion .short 0 @ MinorOsVersion - .short 0 @ MajorImageVersion - .short 0 @ MinorImageVersion + .short LINUX_EFISTUB_MAJOR_VERSION @ MajorImageVersion + .short LINUX_EFISTUB_MINOR_VERSION @ MinorImageVersion .short 0 @ MajorSubsystemVersion .short 0 @ MinorSubsystemVersion .long 0 @ Win32VersionValue diff --git a/arch/arm64/kernel/efi-header.S b/arch/arm64/kernel/efi-header.S index 40c704c5d3a5..914999ccaf8a 100644 --- a/arch/arm64/kernel/efi-header.S +++ b/arch/arm64/kernel/efi-header.S @@ -36,8 +36,8 @@ extra_header_fields: .long PECOFF_FILE_ALIGNMENT // FileAlignment .short 0 // MajorOperatingSystemVersion .short 0 // MinorOperatingSystemVersion - .short 0 // MajorImageVersion - .short 0 // MinorImageVersion + .short LINUX_EFISTUB_MAJOR_VERSION // MajorImageVersion + .short LINUX_EFISTUB_MINOR_VERSION // MinorImageVersion .short 0 // MajorSubsystemVersion .short 0 // MinorSubsystemVersion .long 0 // Win32VersionValue diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index f65661a58cf8..4ee25e28996f 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -147,8 +147,8 @@ extra_header_fields: .long 0x20 # FileAlignment .word 0 # MajorOperatingSystemVersion .word 0 # MinorOperatingSystemVersion - .word 0 # MajorImageVersion - .word 0 # MinorImageVersion + .word LINUX_EFISTUB_MAJOR_VERSION # MajorImageVersion + .word LINUX_EFISTUB_MINOR_VERSION # MinorImageVersion .word 0 # MajorSubsystemVersion .word 0 # MinorSubsystemVersion .long 0 # Win32VersionValue diff --git a/include/linux/pe.h b/include/linux/pe.h index c86bd3a2f70f..e0869f3eadd6 100644 --- a/include/linux/pe.h +++ b/include/linux/pe.h @@ -10,6 +10,9 @@ #include +#define LINUX_EFISTUB_MAJOR_VERSION 0x0 +#define LINUX_EFISTUB_MINOR_VERSION 0x0 + #define MZ_MAGIC 0x5a4d /* "MZ" */ #define PE_MAGIC 0x00004550 /* "PE\0\0" */ From dc235d62fc60a6549238eda7ff29769457fe5663 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Feb 2020 11:57:20 +0100 Subject: [PATCH 0546/2231] efi: Bump the Linux EFI stub major version number to #1 Now that we have introduced new, generic ways for the OS loader to interface with Linux kernels during boot, we need to record this fact in a way that allows loaders to discover this information, and fall back to the existing methods for older kernels. Signed-off-by: Ard Biesheuvel --- include/linux/pe.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/include/linux/pe.h b/include/linux/pe.h index e0869f3eadd6..8ad71d763a77 100644 --- a/include/linux/pe.h +++ b/include/linux/pe.h @@ -10,7 +10,25 @@ #include -#define LINUX_EFISTUB_MAJOR_VERSION 0x0 +/* + * Linux EFI stub v1.0 adds the following functionality: + * - Loading initrd from the LINUX_EFI_INITRD_MEDIA_GUID device path, + * - Loading/starting the kernel from firmware that targets a different + * machine type, via the entrypoint exposed in the .compat PE/COFF section. + * + * The recommended way of loading and starting v1.0 or later kernels is to use + * the LoadImage() and StartImage() EFI boot services, and expose the initrd + * via the LINUX_EFI_INITRD_MEDIA_GUID device path. + * + * Versions older than v1.0 support initrd loading via the image load options + * (using initrd=, limited to the volume from which the kernel itself was + * loaded), or via arch specific means (bootparams, DT, etc). + * + * On x86, LoadImage() and StartImage() can be omitted if the EFI handover + * protocol is implemented, which can be inferred from the version, + * handover_offset and xloadflags fields in the bootparams structure. + */ +#define LINUX_EFISTUB_MAJOR_VERSION 0x1 #define LINUX_EFISTUB_MINOR_VERSION 0x0 #define MZ_MAGIC 0x5a4d /* "MZ" */ From f22aef4afb0d6cc22e408d8254cf6d02d7982ef1 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 24 Feb 2020 09:52:12 +0000 Subject: [PATCH 0547/2231] sched/numa: Trace when no candidate CPU was found on the preferred node sched:sched_stick_numa is meant to fire when a task is unable to migrate to the preferred node. The case where no candidate CPU could be found is not traced which is an important gap. The tracepoint is not fired when the task is not allowed to run on any CPU on the preferred node or the task is already running on the target CPU but neither are interesting corner cases. Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Cc: Steven Rostedt Cc: Vincent Guittot Cc: Juri Lelli Cc: Dietmar Eggemann Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-3-mgorman@techsingularity.net --- kernel/sched/fair.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f38ff5a335d3..f524ce3cea82 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1848,8 +1848,10 @@ static int task_numa_migrate(struct task_struct *p) } /* No better CPU than the current one was found. */ - if (env.best_cpu == -1) + if (env.best_cpu == -1) { + trace_sched_stick_numa(p, env.src_cpu, -1); return -EAGAIN; + } best_rq = cpu_rq(env.best_cpu); if (env.best_task == NULL) { From b2b2042b204796190af7c20069ab790a614c36d0 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 24 Feb 2020 09:52:13 +0000 Subject: [PATCH 0548/2231] sched/numa: Distinguish between the different task_numa_migrate() failure cases sched:sched_stick_numa is meant to fire when a task is unable to migrate to the preferred node but from the trace, it's possibile to tell the difference between "no CPU found", "migration to idle CPU failed" and "tasks could not be swapped". Extend the tracepoint accordingly. Signed-off-by: Mel Gorman [ Minor edits. ] Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Cc: Steven Rostedt Cc: Vincent Guittot Cc: Juri Lelli Cc: Dietmar Eggemann Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-4-mgorman@techsingularity.net --- include/trace/events/sched.h | 49 ++++++++++++++++++++---------------- kernel/sched/fair.c | 6 ++--- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 420e80e56e55..9c3ebb7c83a5 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -487,7 +487,11 @@ TRACE_EVENT(sched_process_hang, ); #endif /* CONFIG_DETECT_HUNG_TASK */ -DECLARE_EVENT_CLASS(sched_move_task_template, +/* + * Tracks migration of tasks from one runqueue to another. Can be used to + * detect if automatic NUMA balancing is bouncing between nodes. + */ +TRACE_EVENT(sched_move_numa, TP_PROTO(struct task_struct *tsk, int src_cpu, int dst_cpu), @@ -519,23 +523,7 @@ DECLARE_EVENT_CLASS(sched_move_task_template, __entry->dst_cpu, __entry->dst_nid) ); -/* - * Tracks migration of tasks from one runqueue to another. Can be used to - * detect if automatic NUMA balancing is bouncing between nodes - */ -DEFINE_EVENT(sched_move_task_template, sched_move_numa, - TP_PROTO(struct task_struct *tsk, int src_cpu, int dst_cpu), - - TP_ARGS(tsk, src_cpu, dst_cpu) -); - -DEFINE_EVENT(sched_move_task_template, sched_stick_numa, - TP_PROTO(struct task_struct *tsk, int src_cpu, int dst_cpu), - - TP_ARGS(tsk, src_cpu, dst_cpu) -); - -TRACE_EVENT(sched_swap_numa, +DECLARE_EVENT_CLASS(sched_numa_pair_template, TP_PROTO(struct task_struct *src_tsk, int src_cpu, struct task_struct *dst_tsk, int dst_cpu), @@ -561,11 +549,11 @@ TRACE_EVENT(sched_swap_numa, __entry->src_ngid = task_numa_group_id(src_tsk); __entry->src_cpu = src_cpu; __entry->src_nid = cpu_to_node(src_cpu); - __entry->dst_pid = task_pid_nr(dst_tsk); - __entry->dst_tgid = task_tgid_nr(dst_tsk); - __entry->dst_ngid = task_numa_group_id(dst_tsk); + __entry->dst_pid = dst_tsk ? task_pid_nr(dst_tsk) : 0; + __entry->dst_tgid = dst_tsk ? task_tgid_nr(dst_tsk) : 0; + __entry->dst_ngid = dst_tsk ? task_numa_group_id(dst_tsk) : 0; __entry->dst_cpu = dst_cpu; - __entry->dst_nid = cpu_to_node(dst_cpu); + __entry->dst_nid = dst_cpu >= 0 ? cpu_to_node(dst_cpu) : -1; ), TP_printk("src_pid=%d src_tgid=%d src_ngid=%d src_cpu=%d src_nid=%d dst_pid=%d dst_tgid=%d dst_ngid=%d dst_cpu=%d dst_nid=%d", @@ -575,6 +563,23 @@ TRACE_EVENT(sched_swap_numa, __entry->dst_cpu, __entry->dst_nid) ); +DEFINE_EVENT(sched_numa_pair_template, sched_stick_numa, + + TP_PROTO(struct task_struct *src_tsk, int src_cpu, + struct task_struct *dst_tsk, int dst_cpu), + + TP_ARGS(src_tsk, src_cpu, dst_tsk, dst_cpu) +); + +DEFINE_EVENT(sched_numa_pair_template, sched_swap_numa, + + TP_PROTO(struct task_struct *src_tsk, int src_cpu, + struct task_struct *dst_tsk, int dst_cpu), + + TP_ARGS(src_tsk, src_cpu, dst_tsk, dst_cpu) +); + + /* * Tracepoint for waking a polling cpu without an IPI. */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f524ce3cea82..5d9c23c134af 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1849,7 +1849,7 @@ static int task_numa_migrate(struct task_struct *p) /* No better CPU than the current one was found. */ if (env.best_cpu == -1) { - trace_sched_stick_numa(p, env.src_cpu, -1); + trace_sched_stick_numa(p, env.src_cpu, NULL, -1); return -EAGAIN; } @@ -1858,7 +1858,7 @@ static int task_numa_migrate(struct task_struct *p) ret = migrate_task_to(p, env.best_cpu); WRITE_ONCE(best_rq->numa_migrate_on, 0); if (ret != 0) - trace_sched_stick_numa(p, env.src_cpu, env.best_cpu); + trace_sched_stick_numa(p, env.src_cpu, NULL, env.best_cpu); return ret; } @@ -1866,7 +1866,7 @@ static int task_numa_migrate(struct task_struct *p) WRITE_ONCE(best_rq->numa_migrate_on, 0); if (ret != 0) - trace_sched_stick_numa(p, env.src_cpu, task_cpu(env.best_task)); + trace_sched_stick_numa(p, env.src_cpu, env.best_task, env.best_cpu); put_task_struct(env.best_task); return ret; } From 6d4d22468dae3d8757af9f8b81b848a76ef4409d Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 24 Feb 2020 09:52:14 +0000 Subject: [PATCH 0549/2231] sched/fair: Reorder enqueue/dequeue_task_fair path The walk through the cgroup hierarchy during the enqueue/dequeue of a task is split in 2 distinct parts for throttled cfs_rq without any added value but making code less readable. Change the code ordering such that everything related to a cfs_rq (throttled or not) will be done in the same loop. In addition, the same steps ordering is used when updating a cfs_rq: - update_load_avg - update_cfs_group - update *h_nr_running This reordering enables the use of h_nr_running in PELT algorithm. No functional and performance changes are expected and have been noticed during tests. Signed-off-by: Vincent Guittot Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Reviewed-by: "Dietmar Eggemann " Acked-by: Peter Zijlstra Cc: Juri Lelli Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-5-mgorman@techsingularity.net --- kernel/sched/fair.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5d9c23c134af..a6c7f8bfc0e5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5260,32 +5260,31 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) cfs_rq = cfs_rq_of(se); enqueue_entity(cfs_rq, se, flags); - /* - * end evaluation on encountering a throttled cfs_rq - * - * note: in the case of encountering a throttled cfs_rq we will - * post the final h_nr_running increment below. - */ - if (cfs_rq_throttled(cfs_rq)) - break; cfs_rq->h_nr_running++; cfs_rq->idle_h_nr_running += idle_h_nr_running; + /* end evaluation on encountering a throttled cfs_rq */ + if (cfs_rq_throttled(cfs_rq)) + goto enqueue_throttle; + flags = ENQUEUE_WAKEUP; } for_each_sched_entity(se) { cfs_rq = cfs_rq_of(se); - cfs_rq->h_nr_running++; - cfs_rq->idle_h_nr_running += idle_h_nr_running; + /* end evaluation on encountering a throttled cfs_rq */ if (cfs_rq_throttled(cfs_rq)) - break; + goto enqueue_throttle; update_load_avg(cfs_rq, se, UPDATE_TG); update_cfs_group(se); + + cfs_rq->h_nr_running++; + cfs_rq->idle_h_nr_running += idle_h_nr_running; } +enqueue_throttle: if (!se) { add_nr_running(rq, 1); /* @@ -5346,17 +5345,13 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) cfs_rq = cfs_rq_of(se); dequeue_entity(cfs_rq, se, flags); - /* - * end evaluation on encountering a throttled cfs_rq - * - * note: in the case of encountering a throttled cfs_rq we will - * post the final h_nr_running decrement below. - */ - if (cfs_rq_throttled(cfs_rq)) - break; cfs_rq->h_nr_running--; cfs_rq->idle_h_nr_running -= idle_h_nr_running; + /* end evaluation on encountering a throttled cfs_rq */ + if (cfs_rq_throttled(cfs_rq)) + goto dequeue_throttle; + /* Don't dequeue parent if it has other entities besides us */ if (cfs_rq->load.weight) { /* Avoid re-evaluating load for this entity: */ @@ -5374,16 +5369,19 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) for_each_sched_entity(se) { cfs_rq = cfs_rq_of(se); - cfs_rq->h_nr_running--; - cfs_rq->idle_h_nr_running -= idle_h_nr_running; + /* end evaluation on encountering a throttled cfs_rq */ if (cfs_rq_throttled(cfs_rq)) - break; + goto dequeue_throttle; update_load_avg(cfs_rq, se, UPDATE_TG); update_cfs_group(se); + + cfs_rq->h_nr_running--; + cfs_rq->idle_h_nr_running -= idle_h_nr_running; } +dequeue_throttle: if (!se) sub_nr_running(rq, 1); From 6499b1b2dd1b8d404a16b9fbbf1af6b9b3c1d83d Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 24 Feb 2020 09:52:15 +0000 Subject: [PATCH 0550/2231] sched/numa: Replace runnable_load_avg by load_avg Similarly to what has been done for the normal load balancer, we can replace runnable_load_avg by load_avg in numa load balancing and track the other statistics like the utilization and the number of running tasks to get to better view of the current state of a node. Signed-off-by: Vincent Guittot Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Reviewed-by: "Dietmar Eggemann " Acked-by: Peter Zijlstra Cc: Juri Lelli Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-6-mgorman@techsingularity.net --- kernel/sched/fair.c | 102 ++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index a6c7f8bfc0e5..bc3d6518a06c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1473,38 +1473,35 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page, group_faults_cpu(ng, src_nid) * group_faults(p, dst_nid) * 4; } -static inline unsigned long cfs_rq_runnable_load_avg(struct cfs_rq *cfs_rq); - -static unsigned long cpu_runnable_load(struct rq *rq) -{ - return cfs_rq_runnable_load_avg(&rq->cfs); -} +/* + * 'numa_type' describes the node at the moment of load balancing. + */ +enum numa_type { + /* The node has spare capacity that can be used to run more tasks. */ + node_has_spare = 0, + /* + * The node is fully used and the tasks don't compete for more CPU + * cycles. Nevertheless, some tasks might wait before running. + */ + node_fully_busy, + /* + * The node is overloaded and can't provide expected CPU cycles to all + * tasks. + */ + node_overloaded +}; /* Cached statistics for all CPUs within a node */ struct numa_stats { unsigned long load; - + unsigned long util; /* Total compute capacity of CPUs on a node */ unsigned long compute_capacity; + unsigned int nr_running; + unsigned int weight; + enum numa_type node_type; }; -/* - * XXX borrowed from update_sg_lb_stats - */ -static void update_numa_stats(struct numa_stats *ns, int nid) -{ - int cpu; - - memset(ns, 0, sizeof(*ns)); - for_each_cpu(cpu, cpumask_of_node(nid)) { - struct rq *rq = cpu_rq(cpu); - - ns->load += cpu_runnable_load(rq); - ns->compute_capacity += capacity_of(cpu); - } - -} - struct task_numa_env { struct task_struct *p; @@ -1521,6 +1518,47 @@ struct task_numa_env { int best_cpu; }; +static unsigned long cpu_load(struct rq *rq); +static unsigned long cpu_util(int cpu); + +static inline enum +numa_type numa_classify(unsigned int imbalance_pct, + struct numa_stats *ns) +{ + if ((ns->nr_running > ns->weight) && + ((ns->compute_capacity * 100) < (ns->util * imbalance_pct))) + return node_overloaded; + + if ((ns->nr_running < ns->weight) || + ((ns->compute_capacity * 100) > (ns->util * imbalance_pct))) + return node_has_spare; + + return node_fully_busy; +} + +/* + * XXX borrowed from update_sg_lb_stats + */ +static void update_numa_stats(struct task_numa_env *env, + struct numa_stats *ns, int nid) +{ + int cpu; + + memset(ns, 0, sizeof(*ns)); + for_each_cpu(cpu, cpumask_of_node(nid)) { + struct rq *rq = cpu_rq(cpu); + + ns->load += cpu_load(rq); + ns->util += cpu_util(cpu); + ns->nr_running += rq->cfs.h_nr_running; + ns->compute_capacity += capacity_of(cpu); + } + + ns->weight = cpumask_weight(cpumask_of_node(nid)); + + ns->node_type = numa_classify(env->imbalance_pct, ns); +} + static void task_numa_assign(struct task_numa_env *env, struct task_struct *p, long imp) { @@ -1556,6 +1594,11 @@ static bool load_too_imbalanced(long src_load, long dst_load, long orig_src_load, orig_dst_load; long src_capacity, dst_capacity; + + /* If dst node has spare capacity, there is no real load imbalance */ + if (env->dst_stats.node_type == node_has_spare) + return false; + /* * The load is corrected for the CPU capacity available on each node. * @@ -1788,10 +1831,10 @@ static int task_numa_migrate(struct task_struct *p) dist = env.dist = node_distance(env.src_nid, env.dst_nid); taskweight = task_weight(p, env.src_nid, dist); groupweight = group_weight(p, env.src_nid, dist); - update_numa_stats(&env.src_stats, env.src_nid); + update_numa_stats(&env, &env.src_stats, env.src_nid); taskimp = task_weight(p, env.dst_nid, dist) - taskweight; groupimp = group_weight(p, env.dst_nid, dist) - groupweight; - update_numa_stats(&env.dst_stats, env.dst_nid); + update_numa_stats(&env, &env.dst_stats, env.dst_nid); /* Try to find a spot on the preferred nid. */ task_numa_find_cpu(&env, taskimp, groupimp); @@ -1824,7 +1867,7 @@ static int task_numa_migrate(struct task_struct *p) env.dist = dist; env.dst_nid = nid; - update_numa_stats(&env.dst_stats, env.dst_nid); + update_numa_stats(&env, &env.dst_stats, env.dst_nid); task_numa_find_cpu(&env, taskimp, groupimp); } } @@ -3686,11 +3729,6 @@ static void remove_entity_load_avg(struct sched_entity *se) raw_spin_unlock_irqrestore(&cfs_rq->removed.lock, flags); } -static inline unsigned long cfs_rq_runnable_load_avg(struct cfs_rq *cfs_rq) -{ - return cfs_rq->avg.runnable_load_avg; -} - static inline unsigned long cfs_rq_load_avg(struct cfs_rq *cfs_rq) { return cfs_rq->avg.load_avg; From fb86f5b2119245afd339280099b4e9417cc0b03a Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 24 Feb 2020 09:52:16 +0000 Subject: [PATCH 0551/2231] sched/numa: Use similar logic to the load balancer for moving between domains with spare capacity The standard load balancer generally tries to keep the number of running tasks or idle CPUs balanced between NUMA domains. The NUMA balancer allows tasks to move if there is spare capacity but this causes a conflict and utilisation between NUMA nodes gets badly skewed. This patch uses similar logic between the NUMA balancer and load balancer when deciding if a task migrating to its preferred node can use an idle CPU. Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Cc: Vincent Guittot Cc: Juri Lelli Cc: Dietmar Eggemann Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-7-mgorman@techsingularity.net --- kernel/sched/fair.c | 81 ++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index bc3d6518a06c..7a3c66f1762f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1520,6 +1520,7 @@ struct task_numa_env { static unsigned long cpu_load(struct rq *rq); static unsigned long cpu_util(int cpu); +static inline long adjust_numa_imbalance(int imbalance, int src_nr_running); static inline enum numa_type numa_classify(unsigned int imbalance_pct, @@ -1594,11 +1595,6 @@ static bool load_too_imbalanced(long src_load, long dst_load, long orig_src_load, orig_dst_load; long src_capacity, dst_capacity; - - /* If dst node has spare capacity, there is no real load imbalance */ - if (env->dst_stats.node_type == node_has_spare) - return false; - /* * The load is corrected for the CPU capacity available on each node. * @@ -1757,19 +1753,42 @@ unlock: static void task_numa_find_cpu(struct task_numa_env *env, long taskimp, long groupimp) { - long src_load, dst_load, load; bool maymove = false; int cpu; - load = task_h_load(env->p); - dst_load = env->dst_stats.load + load; - src_load = env->src_stats.load - load; - /* - * If the improvement from just moving env->p direction is better - * than swapping tasks around, check if a move is possible. + * If dst node has spare capacity, then check if there is an + * imbalance that would be overruled by the load balancer. */ - maymove = !load_too_imbalanced(src_load, dst_load, env); + if (env->dst_stats.node_type == node_has_spare) { + unsigned int imbalance; + int src_running, dst_running; + + /* + * Would movement cause an imbalance? Note that if src has + * more running tasks that the imbalance is ignored as the + * move improves the imbalance from the perspective of the + * CPU load balancer. + * */ + src_running = env->src_stats.nr_running - 1; + dst_running = env->dst_stats.nr_running + 1; + imbalance = max(0, dst_running - src_running); + imbalance = adjust_numa_imbalance(imbalance, src_running); + + /* Use idle CPU if there is no imbalance */ + if (!imbalance) + maymove = true; + } else { + long src_load, dst_load, load; + /* + * If the improvement from just moving env->p direction is better + * than swapping tasks around, check if a move is possible. + */ + load = task_h_load(env->p); + dst_load = env->dst_stats.load + load; + src_load = env->src_stats.load - load; + maymove = !load_too_imbalanced(src_load, dst_load, env); + } for_each_cpu(cpu, cpumask_of_node(env->dst_nid)) { /* Skip this CPU if the source task cannot migrate */ @@ -8694,6 +8713,21 @@ next_group: } } +static inline long adjust_numa_imbalance(int imbalance, int src_nr_running) +{ + unsigned int imbalance_min; + + /* + * Allow a small imbalance based on a simple pair of communicating + * tasks that remain local when the source domain is almost idle. + */ + imbalance_min = 2; + if (src_nr_running <= imbalance_min) + return 0; + + return imbalance; +} + /** * calculate_imbalance - Calculate the amount of imbalance present within the * groups of a given sched_domain during load balance. @@ -8790,24 +8824,9 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s } /* Consider allowing a small imbalance between NUMA groups */ - if (env->sd->flags & SD_NUMA) { - unsigned int imbalance_min; - - /* - * Compute an allowed imbalance based on a simple - * pair of communicating tasks that should remain - * local and ignore them. - * - * NOTE: Generally this would have been based on - * the domain size and this was evaluated. However, - * the benefit is similar across a range of workloads - * and machines but scaling by the domain size adds - * the risk that lower domains have to be rebalanced. - */ - imbalance_min = 2; - if (busiest->sum_nr_running <= imbalance_min) - env->imbalance = 0; - } + if (env->sd->flags & SD_NUMA) + env->imbalance = adjust_numa_imbalance(env->imbalance, + busiest->sum_nr_running); return; } From 0dacee1bfa70e171be3a12a30414c228453048d2 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 24 Feb 2020 09:52:17 +0000 Subject: [PATCH 0552/2231] sched/pelt: Remove unused runnable load average Now that runnable_load_avg is no more used, we can remove it to make space for a new signal. Signed-off-by: Vincent Guittot Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Reviewed-by: "Dietmar Eggemann " Acked-by: Peter Zijlstra Cc: Juri Lelli Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-8-mgorman@techsingularity.net --- include/linux/sched.h | 5 +- kernel/sched/core.c | 2 - kernel/sched/debug.c | 8 --- kernel/sched/fair.c | 130 ++++++------------------------------------ kernel/sched/pelt.c | 62 ++++++++------------ kernel/sched/sched.h | 7 +-- 6 files changed, 43 insertions(+), 171 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index 04278493bf15..037eaffabc24 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -357,7 +357,7 @@ struct util_est { /* * The load_avg/util_avg accumulates an infinite geometric series - * (see __update_load_avg() in kernel/sched/fair.c). + * (see __update_load_avg_cfs_rq() in kernel/sched/pelt.c). * * [load_avg definition] * @@ -401,11 +401,9 @@ struct util_est { struct sched_avg { u64 last_update_time; u64 load_sum; - u64 runnable_load_sum; u32 util_sum; u32 period_contrib; unsigned long load_avg; - unsigned long runnable_load_avg; unsigned long util_avg; struct util_est util_est; } ____cacheline_aligned; @@ -449,7 +447,6 @@ struct sched_statistics { struct sched_entity { /* For load-balancing: */ struct load_weight load; - unsigned long runnable_weight; struct rb_node run_node; struct list_head group_node; unsigned int on_rq; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index e94819d573be..8e6f38073ab3 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -761,7 +761,6 @@ static void set_load_weight(struct task_struct *p, bool update_load) if (task_has_idle_policy(p)) { load->weight = scale_load(WEIGHT_IDLEPRIO); load->inv_weight = WMULT_IDLEPRIO; - p->se.runnable_weight = load->weight; return; } @@ -774,7 +773,6 @@ static void set_load_weight(struct task_struct *p, bool update_load) } else { load->weight = scale_load(sched_prio_to_weight[prio]); load->inv_weight = sched_prio_to_wmult[prio]; - p->se.runnable_weight = load->weight; } } diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 879d3ccf3806..cfecaad387c0 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -402,11 +402,9 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group } P(se->load.weight); - P(se->runnable_weight); #ifdef CONFIG_SMP P(se->avg.load_avg); P(se->avg.util_avg); - P(se->avg.runnable_load_avg); #endif #undef PN_SCHEDSTAT @@ -524,11 +522,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) SEQ_printf(m, " .%-30s: %d\n", "nr_running", cfs_rq->nr_running); SEQ_printf(m, " .%-30s: %ld\n", "load", cfs_rq->load.weight); #ifdef CONFIG_SMP - SEQ_printf(m, " .%-30s: %ld\n", "runnable_weight", cfs_rq->runnable_weight); SEQ_printf(m, " .%-30s: %lu\n", "load_avg", cfs_rq->avg.load_avg); - SEQ_printf(m, " .%-30s: %lu\n", "runnable_load_avg", - cfs_rq->avg.runnable_load_avg); SEQ_printf(m, " .%-30s: %lu\n", "util_avg", cfs_rq->avg.util_avg); SEQ_printf(m, " .%-30s: %u\n", "util_est_enqueued", @@ -947,13 +942,10 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns, "nr_involuntary_switches", (long long)p->nivcsw); P(se.load.weight); - P(se.runnable_weight); #ifdef CONFIG_SMP P(se.avg.load_sum); - P(se.avg.runnable_load_sum); P(se.avg.util_sum); P(se.avg.load_avg); - P(se.avg.runnable_load_avg); P(se.avg.util_avg); P(se.avg.last_update_time); P(se.avg.util_est.ewma); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7a3c66f1762f..b0fb3d6a6185 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -741,9 +741,7 @@ void init_entity_runnable_average(struct sched_entity *se) * nothing has been attached to the task group yet. */ if (entity_is_task(se)) - sa->runnable_load_avg = sa->load_avg = scale_load_down(se->load.weight); - - se->runnable_weight = se->load.weight; + sa->load_avg = scale_load_down(se->load.weight); /* when this task enqueue'ed, it will contribute to its cfs_rq's load_avg */ } @@ -2898,25 +2896,6 @@ account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se) } while (0) #ifdef CONFIG_SMP -static inline void -enqueue_runnable_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) -{ - cfs_rq->runnable_weight += se->runnable_weight; - - cfs_rq->avg.runnable_load_avg += se->avg.runnable_load_avg; - cfs_rq->avg.runnable_load_sum += se_runnable(se) * se->avg.runnable_load_sum; -} - -static inline void -dequeue_runnable_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) -{ - cfs_rq->runnable_weight -= se->runnable_weight; - - sub_positive(&cfs_rq->avg.runnable_load_avg, se->avg.runnable_load_avg); - sub_positive(&cfs_rq->avg.runnable_load_sum, - se_runnable(se) * se->avg.runnable_load_sum); -} - static inline void enqueue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { @@ -2932,28 +2911,22 @@ dequeue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) } #else static inline void -enqueue_runnable_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } -static inline void -dequeue_runnable_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } -static inline void enqueue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } static inline void dequeue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } #endif static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, - unsigned long weight, unsigned long runnable) + unsigned long weight) { if (se->on_rq) { /* commit outstanding execution time */ if (cfs_rq->curr == se) update_curr(cfs_rq); account_entity_dequeue(cfs_rq, se); - dequeue_runnable_load_avg(cfs_rq, se); } dequeue_load_avg(cfs_rq, se); - se->runnable_weight = runnable; update_load_set(&se->load, weight); #ifdef CONFIG_SMP @@ -2961,16 +2934,13 @@ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, u32 divider = LOAD_AVG_MAX - 1024 + se->avg.period_contrib; se->avg.load_avg = div_u64(se_weight(se) * se->avg.load_sum, divider); - se->avg.runnable_load_avg = - div_u64(se_runnable(se) * se->avg.runnable_load_sum, divider); } while (0); #endif enqueue_load_avg(cfs_rq, se); - if (se->on_rq) { + if (se->on_rq) account_entity_enqueue(cfs_rq, se); - enqueue_runnable_load_avg(cfs_rq, se); - } + } void reweight_task(struct task_struct *p, int prio) @@ -2980,7 +2950,7 @@ void reweight_task(struct task_struct *p, int prio) struct load_weight *load = &se->load; unsigned long weight = scale_load(sched_prio_to_weight[prio]); - reweight_entity(cfs_rq, se, weight, weight); + reweight_entity(cfs_rq, se, weight); load->inv_weight = sched_prio_to_wmult[prio]; } @@ -3092,50 +3062,6 @@ static long calc_group_shares(struct cfs_rq *cfs_rq) */ return clamp_t(long, shares, MIN_SHARES, tg_shares); } - -/* - * This calculates the effective runnable weight for a group entity based on - * the group entity weight calculated above. - * - * Because of the above approximation (2), our group entity weight is - * an load_avg based ratio (3). This means that it includes blocked load and - * does not represent the runnable weight. - * - * Approximate the group entity's runnable weight per ratio from the group - * runqueue: - * - * grq->avg.runnable_load_avg - * ge->runnable_weight = ge->load.weight * -------------------------- (7) - * grq->avg.load_avg - * - * However, analogous to above, since the avg numbers are slow, this leads to - * transients in the from-idle case. Instead we use: - * - * ge->runnable_weight = ge->load.weight * - * - * max(grq->avg.runnable_load_avg, grq->runnable_weight) - * ----------------------------------------------------- (8) - * max(grq->avg.load_avg, grq->load.weight) - * - * Where these max() serve both to use the 'instant' values to fix the slow - * from-idle and avoid the /0 on to-idle, similar to (6). - */ -static long calc_group_runnable(struct cfs_rq *cfs_rq, long shares) -{ - long runnable, load_avg; - - load_avg = max(cfs_rq->avg.load_avg, - scale_load_down(cfs_rq->load.weight)); - - runnable = max(cfs_rq->avg.runnable_load_avg, - scale_load_down(cfs_rq->runnable_weight)); - - runnable *= shares; - if (load_avg) - runnable /= load_avg; - - return clamp_t(long, runnable, MIN_SHARES, shares); -} #endif /* CONFIG_SMP */ static inline int throttled_hierarchy(struct cfs_rq *cfs_rq); @@ -3147,7 +3073,7 @@ static inline int throttled_hierarchy(struct cfs_rq *cfs_rq); static void update_cfs_group(struct sched_entity *se) { struct cfs_rq *gcfs_rq = group_cfs_rq(se); - long shares, runnable; + long shares; if (!gcfs_rq) return; @@ -3156,16 +3082,15 @@ static void update_cfs_group(struct sched_entity *se) return; #ifndef CONFIG_SMP - runnable = shares = READ_ONCE(gcfs_rq->tg->shares); + shares = READ_ONCE(gcfs_rq->tg->shares); if (likely(se->load.weight == shares)) return; #else shares = calc_group_shares(gcfs_rq); - runnable = calc_group_runnable(gcfs_rq, shares); #endif - reweight_entity(cfs_rq_of(se), se, shares, runnable); + reweight_entity(cfs_rq_of(se), se, shares); } #else /* CONFIG_FAIR_GROUP_SCHED */ @@ -3290,11 +3215,11 @@ void set_task_rq_fair(struct sched_entity *se, * _IFF_ we look at the pure running and runnable sums. Because they * represent the very same entity, just at different points in the hierarchy. * - * Per the above update_tg_cfs_util() is trivial and simply copies the running - * sum over (but still wrong, because the group entity and group rq do not have - * their PELT windows aligned). + * Per the above update_tg_cfs_util() is trivial * and simply copies the + * running sum over (but still wrong, because the group entity and group rq do + * not have their PELT windows aligned). * - * However, update_tg_cfs_runnable() is more complex. So we have: + * However, update_tg_cfs_load() is more complex. So we have: * * ge->avg.load_avg = ge->load.weight * ge->avg.runnable_avg (2) * @@ -3375,11 +3300,11 @@ update_tg_cfs_util(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq } static inline void -update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) +update_tg_cfs_load(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) { long delta_avg, running_sum, runnable_sum = gcfs_rq->prop_runnable_sum; - unsigned long runnable_load_avg, load_avg; - u64 runnable_load_sum, load_sum = 0; + unsigned long load_avg; + u64 load_sum = 0; s64 delta_sum; if (!runnable_sum) @@ -3427,20 +3352,6 @@ update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cf se->avg.load_avg = load_avg; add_positive(&cfs_rq->avg.load_avg, delta_avg); add_positive(&cfs_rq->avg.load_sum, delta_sum); - - runnable_load_sum = (s64)se_runnable(se) * runnable_sum; - runnable_load_avg = div_s64(runnable_load_sum, LOAD_AVG_MAX); - - if (se->on_rq) { - delta_sum = runnable_load_sum - - se_weight(se) * se->avg.runnable_load_sum; - delta_avg = runnable_load_avg - se->avg.runnable_load_avg; - add_positive(&cfs_rq->avg.runnable_load_avg, delta_avg); - add_positive(&cfs_rq->avg.runnable_load_sum, delta_sum); - } - - se->avg.runnable_load_sum = runnable_sum; - se->avg.runnable_load_avg = runnable_load_avg; } static inline void add_tg_cfs_propagate(struct cfs_rq *cfs_rq, long runnable_sum) @@ -3468,7 +3379,7 @@ static inline int propagate_entity_load_avg(struct sched_entity *se) add_tg_cfs_propagate(cfs_rq, gcfs_rq->prop_runnable_sum); update_tg_cfs_util(cfs_rq, se, gcfs_rq); - update_tg_cfs_runnable(cfs_rq, se, gcfs_rq); + update_tg_cfs_load(cfs_rq, se, gcfs_rq); trace_pelt_cfs_tp(cfs_rq); trace_pelt_se_tp(se); @@ -3612,8 +3523,6 @@ static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s div_u64(se->avg.load_avg * se->avg.load_sum, se_weight(se)); } - se->avg.runnable_load_sum = se->avg.load_sum; - enqueue_load_avg(cfs_rq, se); cfs_rq->avg.util_avg += se->avg.util_avg; cfs_rq->avg.util_sum += se->avg.util_sum; @@ -4074,14 +3983,12 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) /* * When enqueuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. - * - Add its load to cfs_rq->runnable_avg * - For group_entity, update its weight to reflect the new share of * its group cfs_rq * - Add its new weight to cfs_rq->load.weight */ update_load_avg(cfs_rq, se, UPDATE_TG | DO_ATTACH); update_cfs_group(se); - enqueue_runnable_load_avg(cfs_rq, se); account_entity_enqueue(cfs_rq, se); if (flags & ENQUEUE_WAKEUP) @@ -4158,13 +4065,11 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) /* * When dequeuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. - * - Subtract its load from the cfs_rq->runnable_avg. * - Subtract its previous weight from cfs_rq->load.weight. * - For group entity, update its weight to reflect the new share * of its group cfs_rq. */ update_load_avg(cfs_rq, se, UPDATE_TG); - dequeue_runnable_load_avg(cfs_rq, se); update_stats_dequeue(cfs_rq, se, flags); @@ -7649,9 +7554,6 @@ static inline bool cfs_rq_is_decayed(struct cfs_rq *cfs_rq) if (cfs_rq->avg.util_sum) return false; - if (cfs_rq->avg.runnable_load_sum) - return false; - return true; } diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index bd006b79b360..3eb0ed333dcb 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -108,7 +108,7 @@ static u32 __accumulate_pelt_segments(u64 periods, u32 d1, u32 d3) */ static __always_inline u32 accumulate_sum(u64 delta, struct sched_avg *sa, - unsigned long load, unsigned long runnable, int running) + unsigned long load, int running) { u32 contrib = (u32)delta; /* p == 0 -> delta < 1024 */ u64 periods; @@ -121,8 +121,6 @@ accumulate_sum(u64 delta, struct sched_avg *sa, */ if (periods) { sa->load_sum = decay_load(sa->load_sum, periods); - sa->runnable_load_sum = - decay_load(sa->runnable_load_sum, periods); sa->util_sum = decay_load((u64)(sa->util_sum), periods); /* @@ -148,8 +146,6 @@ accumulate_sum(u64 delta, struct sched_avg *sa, if (load) sa->load_sum += load * contrib; - if (runnable) - sa->runnable_load_sum += runnable * contrib; if (running) sa->util_sum += contrib << SCHED_CAPACITY_SHIFT; @@ -186,7 +182,7 @@ accumulate_sum(u64 delta, struct sched_avg *sa, */ static __always_inline int ___update_load_sum(u64 now, struct sched_avg *sa, - unsigned long load, unsigned long runnable, int running) + unsigned long load, int running) { u64 delta; @@ -222,7 +218,7 @@ ___update_load_sum(u64 now, struct sched_avg *sa, * Also see the comment in accumulate_sum(). */ if (!load) - runnable = running = 0; + running = 0; /* * Now we know we crossed measurement unit boundaries. The *_avg @@ -231,14 +227,14 @@ ___update_load_sum(u64 now, struct sched_avg *sa, * Step 1: accumulate *_sum since last_update_time. If we haven't * crossed period boundaries, finish. */ - if (!accumulate_sum(delta, sa, load, runnable, running)) + if (!accumulate_sum(delta, sa, load, running)) return 0; return 1; } static __always_inline void -___update_load_avg(struct sched_avg *sa, unsigned long load, unsigned long runnable) +___update_load_avg(struct sched_avg *sa, unsigned long load) { u32 divider = LOAD_AVG_MAX - 1024 + sa->period_contrib; @@ -246,7 +242,6 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load, unsigned long runna * Step 2: update *_avg. */ sa->load_avg = div_u64(load * sa->load_sum, divider); - sa->runnable_load_avg = div_u64(runnable * sa->runnable_load_sum, divider); WRITE_ONCE(sa->util_avg, sa->util_sum / divider); } @@ -254,17 +249,13 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load, unsigned long runna * sched_entity: * * task: - * se_runnable() == se_weight() + * se_weight() = se->load.weight * * group: [ see update_cfs_group() ] * se_weight() = tg->weight * grq->load_avg / tg->load_avg - * se_runnable() = se_weight(se) * grq->runnable_load_avg / grq->load_avg * - * load_sum := runnable_sum - * load_avg = se_weight(se) * runnable_avg - * - * runnable_load_sum := runnable_sum - * runnable_load_avg = se_runnable(se) * runnable_avg + * load_sum := runnable + * load_avg = se_weight(se) * load_sum * * XXX collapse load_sum and runnable_load_sum * @@ -272,15 +263,12 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load, unsigned long runna * * load_sum = \Sum se_weight(se) * se->avg.load_sum * load_avg = \Sum se->avg.load_avg - * - * runnable_load_sum = \Sum se_runnable(se) * se->avg.runnable_load_sum - * runnable_load_avg = \Sum se->avg.runable_load_avg */ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) { - if (___update_load_sum(now, &se->avg, 0, 0, 0)) { - ___update_load_avg(&se->avg, se_weight(se), se_runnable(se)); + if (___update_load_sum(now, &se->avg, 0, 0)) { + ___update_load_avg(&se->avg, se_weight(se)); trace_pelt_se_tp(se); return 1; } @@ -290,10 +278,9 @@ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) int __update_load_avg_se(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se) { - if (___update_load_sum(now, &se->avg, !!se->on_rq, !!se->on_rq, - cfs_rq->curr == se)) { + if (___update_load_sum(now, &se->avg, !!se->on_rq, cfs_rq->curr == se)) { - ___update_load_avg(&se->avg, se_weight(se), se_runnable(se)); + ___update_load_avg(&se->avg, se_weight(se)); cfs_se_util_change(&se->avg); trace_pelt_se_tp(se); return 1; @@ -306,10 +293,9 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) { if (___update_load_sum(now, &cfs_rq->avg, scale_load_down(cfs_rq->load.weight), - scale_load_down(cfs_rq->runnable_weight), cfs_rq->curr != NULL)) { - ___update_load_avg(&cfs_rq->avg, 1, 1); + ___update_load_avg(&cfs_rq->avg, 1); trace_pelt_cfs_tp(cfs_rq); return 1; } @@ -322,20 +308,19 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) * * util_sum = \Sum se->avg.util_sum but se->avg.util_sum is not tracked * util_sum = cpu_scale * load_sum - * runnable_load_sum = load_sum + * runnable_sum = util_sum * - * load_avg and runnable_load_avg are not supported and meaningless. + * load_avg is not supported and meaningless. * */ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) { if (___update_load_sum(now, &rq->avg_rt, - running, running, running)) { - ___update_load_avg(&rq->avg_rt, 1, 1); + ___update_load_avg(&rq->avg_rt, 1); trace_pelt_rt_tp(rq); return 1; } @@ -348,18 +333,19 @@ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) * * util_sum = \Sum se->avg.util_sum but se->avg.util_sum is not tracked * util_sum = cpu_scale * load_sum - * runnable_load_sum = load_sum + * runnable_sum = util_sum + * + * load_avg is not supported and meaningless. * */ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) { if (___update_load_sum(now, &rq->avg_dl, - running, running, running)) { - ___update_load_avg(&rq->avg_dl, 1, 1); + ___update_load_avg(&rq->avg_dl, 1); trace_pelt_dl_tp(rq); return 1; } @@ -373,7 +359,9 @@ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) * * util_sum = \Sum se->avg.util_sum but se->avg.util_sum is not tracked * util_sum = cpu_scale * load_sum - * runnable_load_sum = load_sum + * runnable_sum = util_sum + * + * load_avg is not supported and meaningless. * */ @@ -401,16 +389,14 @@ int update_irq_load_avg(struct rq *rq, u64 running) * rq->clock += delta with delta >= running */ ret = ___update_load_sum(rq->clock - running, &rq->avg_irq, - 0, 0, 0); ret += ___update_load_sum(rq->clock, &rq->avg_irq, - 1, 1, 1); if (ret) { - ___update_load_avg(&rq->avg_irq, 1, 1); + ___update_load_avg(&rq->avg_irq, 1); trace_pelt_irq_tp(rq); } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 12bf82d86156..ce27e588fa7c 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -489,7 +489,6 @@ struct cfs_bandwidth { }; /* CFS-related fields in a runqueue */ struct cfs_rq { struct load_weight load; - unsigned long runnable_weight; unsigned int nr_running; unsigned int h_nr_running; /* SCHED_{NORMAL,BATCH,IDLE} */ unsigned int idle_h_nr_running; /* SCHED_IDLE */ @@ -688,8 +687,10 @@ struct dl_rq { #ifdef CONFIG_FAIR_GROUP_SCHED /* An entity is a task if it doesn't "own" a runqueue */ #define entity_is_task(se) (!se->my_q) + #else #define entity_is_task(se) 1 + #endif #ifdef CONFIG_SMP @@ -701,10 +702,6 @@ static inline long se_weight(struct sched_entity *se) return scale_load_down(se->load.weight); } -static inline long se_runnable(struct sched_entity *se) -{ - return scale_load_down(se->runnable_weight); -} static inline bool sched_asym_prefer(int a, int b) { From 9f68395333ad7f5bfe2f83473fed363d4229f11c Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 24 Feb 2020 09:52:18 +0000 Subject: [PATCH 0553/2231] sched/pelt: Add a new runnable average signal Now that runnable_load_avg has been removed, we can replace it by a new signal that will highlight the runnable pressure on a cfs_rq. This signal track the waiting time of tasks on rq and can help to better define the state of rqs. At now, only util_avg is used to define the state of a rq: A rq with more that around 80% of utilization and more than 1 tasks is considered as overloaded. But the util_avg signal of a rq can become temporaly low after that a task migrated onto another rq which can bias the classification of the rq. When tasks compete for the same rq, their runnable average signal will be higher than util_avg as it will include the waiting time and we can use this signal to better classify cfs_rqs. The new runnable_avg will track the runnable time of a task which simply adds the waiting time to the running time. The runnable _avg of cfs_rq will be the /Sum of se's runnable_avg and the runnable_avg of group entity will follow the one of the rq similarly to util_avg. Signed-off-by: Vincent Guittot Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Reviewed-by: "Dietmar Eggemann " Acked-by: Peter Zijlstra Cc: Juri Lelli Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-9-mgorman@techsingularity.net --- include/linux/sched.h | 26 +++++++++------ kernel/sched/debug.c | 9 +++-- kernel/sched/fair.c | 77 +++++++++++++++++++++++++++++++++++++++---- kernel/sched/pelt.c | 39 +++++++++++++++------- kernel/sched/sched.h | 22 ++++++++++++- 5 files changed, 142 insertions(+), 31 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index 037eaffabc24..2e9199bf947b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -356,28 +356,30 @@ struct util_est { } __attribute__((__aligned__(sizeof(u64)))); /* - * The load_avg/util_avg accumulates an infinite geometric series + * The load/runnable/util_avg accumulates an infinite geometric series * (see __update_load_avg_cfs_rq() in kernel/sched/pelt.c). * * [load_avg definition] * * load_avg = runnable% * scale_load_down(load) * - * where runnable% is the time ratio that a sched_entity is runnable. - * For cfs_rq, it is the aggregated load_avg of all runnable and - * blocked sched_entities. + * [runnable_avg definition] + * + * runnable_avg = runnable% * SCHED_CAPACITY_SCALE * * [util_avg definition] * * util_avg = running% * SCHED_CAPACITY_SCALE * - * where running% is the time ratio that a sched_entity is running on - * a CPU. For cfs_rq, it is the aggregated util_avg of all runnable - * and blocked sched_entities. + * where runnable% is the time ratio that a sched_entity is runnable and + * running% the time ratio that a sched_entity is running. * - * load_avg and util_avg don't direcly factor frequency scaling and CPU - * capacity scaling. The scaling is done through the rq_clock_pelt that - * is used for computing those signals (see update_rq_clock_pelt()) + * For cfs_rq, they are the aggregated values of all runnable and blocked + * sched_entities. + * + * The load/runnable/util_avg doesn't direcly factor frequency scaling and CPU + * capacity scaling. The scaling is done through the rq_clock_pelt that is used + * for computing those signals (see update_rq_clock_pelt()) * * N.B., the above ratios (runnable% and running%) themselves are in the * range of [0, 1]. To do fixed point arithmetics, we therefore scale them @@ -401,9 +403,11 @@ struct util_est { struct sched_avg { u64 last_update_time; u64 load_sum; + u64 runnable_sum; u32 util_sum; u32 period_contrib; unsigned long load_avg; + unsigned long runnable_avg; unsigned long util_avg; struct util_est util_est; } ____cacheline_aligned; @@ -467,6 +471,8 @@ struct sched_entity { struct cfs_rq *cfs_rq; /* rq "owned" by this entity/group: */ struct cfs_rq *my_q; + /* cached value of my_q->h_nr_running */ + unsigned long runnable_weight; #endif #ifdef CONFIG_SMP diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index cfecaad387c0..8331bc04aea2 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -405,6 +405,7 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group #ifdef CONFIG_SMP P(se->avg.load_avg); P(se->avg.util_avg); + P(se->avg.runnable_avg); #endif #undef PN_SCHEDSTAT @@ -524,6 +525,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) #ifdef CONFIG_SMP SEQ_printf(m, " .%-30s: %lu\n", "load_avg", cfs_rq->avg.load_avg); + SEQ_printf(m, " .%-30s: %lu\n", "runnable_avg", + cfs_rq->avg.runnable_avg); SEQ_printf(m, " .%-30s: %lu\n", "util_avg", cfs_rq->avg.util_avg); SEQ_printf(m, " .%-30s: %u\n", "util_est_enqueued", @@ -532,8 +535,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) cfs_rq->removed.load_avg); SEQ_printf(m, " .%-30s: %ld\n", "removed.util_avg", cfs_rq->removed.util_avg); - SEQ_printf(m, " .%-30s: %ld\n", "removed.runnable_sum", - cfs_rq->removed.runnable_sum); + SEQ_printf(m, " .%-30s: %ld\n", "removed.runnable_avg", + cfs_rq->removed.runnable_avg); #ifdef CONFIG_FAIR_GROUP_SCHED SEQ_printf(m, " .%-30s: %lu\n", "tg_load_avg_contrib", cfs_rq->tg_load_avg_contrib); @@ -944,8 +947,10 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns, P(se.load.weight); #ifdef CONFIG_SMP P(se.avg.load_sum); + P(se.avg.runnable_sum); P(se.avg.util_sum); P(se.avg.load_avg); + P(se.avg.runnable_avg); P(se.avg.util_avg); P(se.avg.last_update_time); P(se.avg.util_est.ewma); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b0fb3d6a6185..49b36d62cc35 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -794,6 +794,8 @@ void post_init_entity_util_avg(struct task_struct *p) } } + sa->runnable_avg = cpu_scale; + if (p->sched_class != &fair_sched_class) { /* * For !fair tasks do: @@ -3215,9 +3217,9 @@ void set_task_rq_fair(struct sched_entity *se, * _IFF_ we look at the pure running and runnable sums. Because they * represent the very same entity, just at different points in the hierarchy. * - * Per the above update_tg_cfs_util() is trivial * and simply copies the - * running sum over (but still wrong, because the group entity and group rq do - * not have their PELT windows aligned). + * Per the above update_tg_cfs_util() and update_tg_cfs_runnable() are trivial + * and simply copies the running/runnable sum over (but still wrong, because + * the group entity and group rq do not have their PELT windows aligned). * * However, update_tg_cfs_load() is more complex. So we have: * @@ -3299,6 +3301,32 @@ update_tg_cfs_util(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq cfs_rq->avg.util_sum = cfs_rq->avg.util_avg * LOAD_AVG_MAX; } +static inline void +update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) +{ + long delta = gcfs_rq->avg.runnable_avg - se->avg.runnable_avg; + + /* Nothing to update */ + if (!delta) + return; + + /* + * The relation between sum and avg is: + * + * LOAD_AVG_MAX - 1024 + sa->period_contrib + * + * however, the PELT windows are not aligned between grq and gse. + */ + + /* Set new sched_entity's runnable */ + se->avg.runnable_avg = gcfs_rq->avg.runnable_avg; + se->avg.runnable_sum = se->avg.runnable_avg * LOAD_AVG_MAX; + + /* Update parent cfs_rq runnable */ + add_positive(&cfs_rq->avg.runnable_avg, delta); + cfs_rq->avg.runnable_sum = cfs_rq->avg.runnable_avg * LOAD_AVG_MAX; +} + static inline void update_tg_cfs_load(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) { @@ -3379,6 +3407,7 @@ static inline int propagate_entity_load_avg(struct sched_entity *se) add_tg_cfs_propagate(cfs_rq, gcfs_rq->prop_runnable_sum); update_tg_cfs_util(cfs_rq, se, gcfs_rq); + update_tg_cfs_runnable(cfs_rq, se, gcfs_rq); update_tg_cfs_load(cfs_rq, se, gcfs_rq); trace_pelt_cfs_tp(cfs_rq); @@ -3449,7 +3478,7 @@ static inline void add_tg_cfs_propagate(struct cfs_rq *cfs_rq, long runnable_sum static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) { - unsigned long removed_load = 0, removed_util = 0, removed_runnable_sum = 0; + unsigned long removed_load = 0, removed_util = 0, removed_runnable = 0; struct sched_avg *sa = &cfs_rq->avg; int decayed = 0; @@ -3460,7 +3489,7 @@ update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) raw_spin_lock(&cfs_rq->removed.lock); swap(cfs_rq->removed.util_avg, removed_util); swap(cfs_rq->removed.load_avg, removed_load); - swap(cfs_rq->removed.runnable_sum, removed_runnable_sum); + swap(cfs_rq->removed.runnable_avg, removed_runnable); cfs_rq->removed.nr = 0; raw_spin_unlock(&cfs_rq->removed.lock); @@ -3472,7 +3501,16 @@ update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) sub_positive(&sa->util_avg, r); sub_positive(&sa->util_sum, r * divider); - add_tg_cfs_propagate(cfs_rq, -(long)removed_runnable_sum); + r = removed_runnable; + sub_positive(&sa->runnable_avg, r); + sub_positive(&sa->runnable_sum, r * divider); + + /* + * removed_runnable is the unweighted version of removed_load so we + * can use it to estimate removed_load_sum. + */ + add_tg_cfs_propagate(cfs_rq, + -(long)(removed_runnable * divider) >> SCHED_CAPACITY_SHIFT); decayed = 1; } @@ -3517,6 +3555,8 @@ static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s */ se->avg.util_sum = se->avg.util_avg * divider; + se->avg.runnable_sum = se->avg.runnable_avg * divider; + se->avg.load_sum = divider; if (se_weight(se)) { se->avg.load_sum = @@ -3526,6 +3566,8 @@ static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s enqueue_load_avg(cfs_rq, se); cfs_rq->avg.util_avg += se->avg.util_avg; cfs_rq->avg.util_sum += se->avg.util_sum; + cfs_rq->avg.runnable_avg += se->avg.runnable_avg; + cfs_rq->avg.runnable_sum += se->avg.runnable_sum; add_tg_cfs_propagate(cfs_rq, se->avg.load_sum); @@ -3547,6 +3589,8 @@ static void detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s dequeue_load_avg(cfs_rq, se); sub_positive(&cfs_rq->avg.util_avg, se->avg.util_avg); sub_positive(&cfs_rq->avg.util_sum, se->avg.util_sum); + sub_positive(&cfs_rq->avg.runnable_avg, se->avg.runnable_avg); + sub_positive(&cfs_rq->avg.runnable_sum, se->avg.runnable_sum); add_tg_cfs_propagate(cfs_rq, -se->avg.load_sum); @@ -3653,10 +3697,15 @@ static void remove_entity_load_avg(struct sched_entity *se) ++cfs_rq->removed.nr; cfs_rq->removed.util_avg += se->avg.util_avg; cfs_rq->removed.load_avg += se->avg.load_avg; - cfs_rq->removed.runnable_sum += se->avg.load_sum; /* == runnable_sum */ + cfs_rq->removed.runnable_avg += se->avg.runnable_avg; raw_spin_unlock_irqrestore(&cfs_rq->removed.lock, flags); } +static inline unsigned long cfs_rq_runnable_avg(struct cfs_rq *cfs_rq) +{ + return cfs_rq->avg.runnable_avg; +} + static inline unsigned long cfs_rq_load_avg(struct cfs_rq *cfs_rq) { return cfs_rq->avg.load_avg; @@ -3983,11 +4032,13 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) /* * When enqueuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. + * - Add its load to cfs_rq->runnable_avg * - For group_entity, update its weight to reflect the new share of * its group cfs_rq * - Add its new weight to cfs_rq->load.weight */ update_load_avg(cfs_rq, se, UPDATE_TG | DO_ATTACH); + se_update_runnable(se); update_cfs_group(se); account_entity_enqueue(cfs_rq, se); @@ -4065,11 +4116,13 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) /* * When dequeuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. + * - Subtract its load from the cfs_rq->runnable_avg. * - Subtract its previous weight from cfs_rq->load.weight. * - For group entity, update its weight to reflect the new share * of its group cfs_rq. */ update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); update_stats_dequeue(cfs_rq, se, flags); @@ -5240,6 +5293,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) goto enqueue_throttle; update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); update_cfs_group(se); cfs_rq->h_nr_running++; @@ -5337,6 +5391,7 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) goto dequeue_throttle; update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); update_cfs_group(se); cfs_rq->h_nr_running--; @@ -5409,6 +5464,11 @@ static unsigned long cpu_load_without(struct rq *rq, struct task_struct *p) return load; } +static unsigned long cpu_runnable(struct rq *rq) +{ + return cfs_rq_runnable_avg(&rq->cfs); +} + static unsigned long capacity_of(int cpu) { return cpu_rq(cpu)->cpu_capacity; @@ -7554,6 +7614,9 @@ static inline bool cfs_rq_is_decayed(struct cfs_rq *cfs_rq) if (cfs_rq->avg.util_sum) return false; + if (cfs_rq->avg.runnable_sum) + return false; + return true; } diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index 3eb0ed333dcb..c40d57a2a248 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -108,7 +108,7 @@ static u32 __accumulate_pelt_segments(u64 periods, u32 d1, u32 d3) */ static __always_inline u32 accumulate_sum(u64 delta, struct sched_avg *sa, - unsigned long load, int running) + unsigned long load, unsigned long runnable, int running) { u32 contrib = (u32)delta; /* p == 0 -> delta < 1024 */ u64 periods; @@ -121,6 +121,8 @@ accumulate_sum(u64 delta, struct sched_avg *sa, */ if (periods) { sa->load_sum = decay_load(sa->load_sum, periods); + sa->runnable_sum = + decay_load(sa->runnable_sum, periods); sa->util_sum = decay_load((u64)(sa->util_sum), periods); /* @@ -146,6 +148,8 @@ accumulate_sum(u64 delta, struct sched_avg *sa, if (load) sa->load_sum += load * contrib; + if (runnable) + sa->runnable_sum += runnable * contrib << SCHED_CAPACITY_SHIFT; if (running) sa->util_sum += contrib << SCHED_CAPACITY_SHIFT; @@ -182,7 +186,7 @@ accumulate_sum(u64 delta, struct sched_avg *sa, */ static __always_inline int ___update_load_sum(u64 now, struct sched_avg *sa, - unsigned long load, int running) + unsigned long load, unsigned long runnable, int running) { u64 delta; @@ -218,7 +222,7 @@ ___update_load_sum(u64 now, struct sched_avg *sa, * Also see the comment in accumulate_sum(). */ if (!load) - running = 0; + runnable = running = 0; /* * Now we know we crossed measurement unit boundaries. The *_avg @@ -227,7 +231,7 @@ ___update_load_sum(u64 now, struct sched_avg *sa, * Step 1: accumulate *_sum since last_update_time. If we haven't * crossed period boundaries, finish. */ - if (!accumulate_sum(delta, sa, load, running)) + if (!accumulate_sum(delta, sa, load, runnable, running)) return 0; return 1; @@ -242,6 +246,7 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load) * Step 2: update *_avg. */ sa->load_avg = div_u64(load * sa->load_sum, divider); + sa->runnable_avg = div_u64(sa->runnable_sum, divider); WRITE_ONCE(sa->util_avg, sa->util_sum / divider); } @@ -250,24 +255,30 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load) * * task: * se_weight() = se->load.weight + * se_runnable() = !!on_rq * * group: [ see update_cfs_group() ] * se_weight() = tg->weight * grq->load_avg / tg->load_avg + * se_runnable() = grq->h_nr_running + * + * runnable_sum = se_runnable() * runnable = grq->runnable_sum + * runnable_avg = runnable_sum * * load_sum := runnable * load_avg = se_weight(se) * load_sum * - * XXX collapse load_sum and runnable_load_sum - * * cfq_rq: * + * runnable_sum = \Sum se->avg.runnable_sum + * runnable_avg = \Sum se->avg.runnable_avg + * * load_sum = \Sum se_weight(se) * se->avg.load_sum * load_avg = \Sum se->avg.load_avg */ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) { - if (___update_load_sum(now, &se->avg, 0, 0)) { + if (___update_load_sum(now, &se->avg, 0, 0, 0)) { ___update_load_avg(&se->avg, se_weight(se)); trace_pelt_se_tp(se); return 1; @@ -278,7 +289,8 @@ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) int __update_load_avg_se(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se) { - if (___update_load_sum(now, &se->avg, !!se->on_rq, cfs_rq->curr == se)) { + if (___update_load_sum(now, &se->avg, !!se->on_rq, se_runnable(se), + cfs_rq->curr == se)) { ___update_load_avg(&se->avg, se_weight(se)); cfs_se_util_change(&se->avg); @@ -293,6 +305,7 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) { if (___update_load_sum(now, &cfs_rq->avg, scale_load_down(cfs_rq->load.weight), + cfs_rq->h_nr_running, cfs_rq->curr != NULL)) { ___update_load_avg(&cfs_rq->avg, 1); @@ -310,13 +323,14 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) * util_sum = cpu_scale * load_sum * runnable_sum = util_sum * - * load_avg is not supported and meaningless. + * load_avg and runnable_avg are not supported and meaningless. * */ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) { if (___update_load_sum(now, &rq->avg_rt, + running, running, running)) { @@ -335,13 +349,14 @@ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) * util_sum = cpu_scale * load_sum * runnable_sum = util_sum * - * load_avg is not supported and meaningless. + * load_avg and runnable_avg are not supported and meaningless. * */ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) { if (___update_load_sum(now, &rq->avg_dl, + running, running, running)) { @@ -361,7 +376,7 @@ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) * util_sum = cpu_scale * load_sum * runnable_sum = util_sum * - * load_avg is not supported and meaningless. + * load_avg and runnable_avg are not supported and meaningless. * */ @@ -389,9 +404,11 @@ int update_irq_load_avg(struct rq *rq, u64 running) * rq->clock += delta with delta >= running */ ret = ___update_load_sum(rq->clock - running, &rq->avg_irq, + 0, 0, 0); ret += ___update_load_sum(rq->clock, &rq->avg_irq, + 1, 1, 1); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index ce27e588fa7c..2a0caf394dd4 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -527,7 +527,7 @@ struct cfs_rq { int nr; unsigned long load_avg; unsigned long util_avg; - unsigned long runnable_sum; + unsigned long runnable_avg; } removed; #ifdef CONFIG_FAIR_GROUP_SCHED @@ -688,9 +688,29 @@ struct dl_rq { /* An entity is a task if it doesn't "own" a runqueue */ #define entity_is_task(se) (!se->my_q) +static inline void se_update_runnable(struct sched_entity *se) +{ + if (!entity_is_task(se)) + se->runnable_weight = se->my_q->h_nr_running; +} + +static inline long se_runnable(struct sched_entity *se) +{ + if (entity_is_task(se)) + return !!se->on_rq; + else + return se->runnable_weight; +} + #else #define entity_is_task(se) 1 +static inline void se_update_runnable(struct sched_entity *se) {} + +static inline long se_runnable(struct sched_entity *se) +{ + return !!se->on_rq; +} #endif #ifdef CONFIG_SMP From 070f5e860ee2bf588c99ef7b4c202451faa48236 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 24 Feb 2020 09:52:19 +0000 Subject: [PATCH 0554/2231] sched/fair: Take into account runnable_avg to classify group Take into account the new runnable_avg signal to classify a group and to mitigate the volatility of util_avg in face of intensive migration or new task with random utilization. Signed-off-by: Vincent Guittot Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Reviewed-by: "Dietmar Eggemann " Acked-by: Peter Zijlstra Cc: Juri Lelli Cc: Steven Rostedt Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-10-mgorman@techsingularity.net --- kernel/sched/fair.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 49b36d62cc35..87521acb3698 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5469,6 +5469,24 @@ static unsigned long cpu_runnable(struct rq *rq) return cfs_rq_runnable_avg(&rq->cfs); } +static unsigned long cpu_runnable_without(struct rq *rq, struct task_struct *p) +{ + struct cfs_rq *cfs_rq; + unsigned int runnable; + + /* Task has no contribution or is new */ + if (cpu_of(rq) != task_cpu(p) || !READ_ONCE(p->se.avg.last_update_time)) + return cpu_runnable(rq); + + cfs_rq = &rq->cfs; + runnable = READ_ONCE(cfs_rq->avg.runnable_avg); + + /* Discount task's runnable from CPU's runnable */ + lsub_positive(&runnable, p->se.avg.runnable_avg); + + return runnable; +} + static unsigned long capacity_of(int cpu) { return cpu_rq(cpu)->cpu_capacity; @@ -7752,7 +7770,8 @@ struct sg_lb_stats { unsigned long avg_load; /*Avg load across the CPUs of the group */ unsigned long group_load; /* Total load over the CPUs of the group */ unsigned long group_capacity; - unsigned long group_util; /* Total utilization of the group */ + unsigned long group_util; /* Total utilization over the CPUs of the group */ + unsigned long group_runnable; /* Total runnable time over the CPUs of the group */ unsigned int sum_nr_running; /* Nr of tasks running in the group */ unsigned int sum_h_nr_running; /* Nr of CFS tasks running in the group */ unsigned int idle_cpus; @@ -7973,6 +7992,10 @@ group_has_capacity(unsigned int imbalance_pct, struct sg_lb_stats *sgs) if (sgs->sum_nr_running < sgs->group_weight) return true; + if ((sgs->group_capacity * imbalance_pct) < + (sgs->group_runnable * 100)) + return false; + if ((sgs->group_capacity * 100) > (sgs->group_util * imbalance_pct)) return true; @@ -7998,6 +8021,10 @@ group_is_overloaded(unsigned int imbalance_pct, struct sg_lb_stats *sgs) (sgs->group_util * imbalance_pct)) return true; + if ((sgs->group_capacity * imbalance_pct) < + (sgs->group_runnable * 100)) + return true; + return false; } @@ -8092,6 +8119,7 @@ static inline void update_sg_lb_stats(struct lb_env *env, sgs->group_load += cpu_load(rq); sgs->group_util += cpu_util(i); + sgs->group_runnable += cpu_runnable(rq); sgs->sum_h_nr_running += rq->cfs.h_nr_running; nr_running = rq->nr_running; @@ -8367,6 +8395,7 @@ static inline void update_sg_wakeup_stats(struct sched_domain *sd, sgs->group_load += cpu_load_without(rq, p); sgs->group_util += cpu_util_without(i, p); + sgs->group_runnable += cpu_runnable_without(rq, p); local = task_running_on_cpu(i, p); sgs->sum_h_nr_running += rq->cfs.h_nr_running - local; From ff7db0bf24db919f69121bf5df8f3cb6d79f49af Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 24 Feb 2020 09:52:20 +0000 Subject: [PATCH 0555/2231] sched/numa: Prefer using an idle CPU as a migration target instead of comparing tasks task_numa_find_cpu() can scan a node multiple times. Minimally it scans to gather statistics and later to find a suitable target. In some cases, the second scan will simply pick an idle CPU if the load is not imbalanced. This patch caches information on an idle core while gathering statistics and uses it immediately if load is not imbalanced to avoid a second scan of the node runqueues. Preference is given to an idle core rather than an idle SMT sibling to avoid packing HT siblings due to linearly scanning the node cpumask. As a side-effect, even when the second scan is necessary, the importance of using select_idle_sibling is much reduced because information on idle CPUs is cached and can be reused. Note that this patch actually makes is harder to move to an idle CPU as multiple tasks can race for the same idle CPU due to a race checking numa_migrate_on. This is addressed in the next patch. Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Cc: Vincent Guittot Cc: Juri Lelli Cc: Dietmar Eggemann Cc: Steven Rostedt Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-11-mgorman@techsingularity.net --- kernel/sched/fair.c | 119 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 17 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 87521acb3698..2da21f44e4d0 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1500,8 +1500,29 @@ struct numa_stats { unsigned int nr_running; unsigned int weight; enum numa_type node_type; + int idle_cpu; }; +static inline bool is_core_idle(int cpu) +{ +#ifdef CONFIG_SCHED_SMT + int sibling; + + for_each_cpu(sibling, cpu_smt_mask(cpu)) { + if (cpu == sibling) + continue; + + if (!idle_cpu(cpu)) + return false; + } +#endif + + return true; +} + +/* Forward declarations of select_idle_sibling helpers */ +static inline bool test_idle_cores(int cpu, bool def); + struct task_numa_env { struct task_struct *p; @@ -1537,15 +1558,39 @@ numa_type numa_classify(unsigned int imbalance_pct, return node_fully_busy; } +static inline int numa_idle_core(int idle_core, int cpu) +{ +#ifdef CONFIG_SCHED_SMT + if (!static_branch_likely(&sched_smt_present) || + idle_core >= 0 || !test_idle_cores(cpu, false)) + return idle_core; + + /* + * Prefer cores instead of packing HT siblings + * and triggering future load balancing. + */ + if (is_core_idle(cpu)) + idle_core = cpu; +#endif + + return idle_core; +} + /* - * XXX borrowed from update_sg_lb_stats + * Gather all necessary information to make NUMA balancing placement + * decisions that are compatible with standard load balancer. This + * borrows code and logic from update_sg_lb_stats but sharing a + * common implementation is impractical. */ static void update_numa_stats(struct task_numa_env *env, - struct numa_stats *ns, int nid) + struct numa_stats *ns, int nid, + bool find_idle) { - int cpu; + int cpu, idle_core = -1; memset(ns, 0, sizeof(*ns)); + ns->idle_cpu = -1; + for_each_cpu(cpu, cpumask_of_node(nid)) { struct rq *rq = cpu_rq(cpu); @@ -1553,11 +1598,25 @@ static void update_numa_stats(struct task_numa_env *env, ns->util += cpu_util(cpu); ns->nr_running += rq->cfs.h_nr_running; ns->compute_capacity += capacity_of(cpu); + + if (find_idle && !rq->nr_running && idle_cpu(cpu)) { + if (READ_ONCE(rq->numa_migrate_on) || + !cpumask_test_cpu(cpu, env->p->cpus_ptr)) + continue; + + if (ns->idle_cpu == -1) + ns->idle_cpu = cpu; + + idle_core = numa_idle_core(idle_core, cpu); + } } ns->weight = cpumask_weight(cpumask_of_node(nid)); ns->node_type = numa_classify(env->imbalance_pct, ns); + + if (idle_core >= 0) + ns->idle_cpu = idle_core; } static void task_numa_assign(struct task_numa_env *env, @@ -1566,7 +1625,7 @@ static void task_numa_assign(struct task_numa_env *env, struct rq *rq = cpu_rq(env->dst_cpu); /* Bail out if run-queue part of active NUMA balance. */ - if (xchg(&rq->numa_migrate_on, 1)) + if (env->best_cpu != env->dst_cpu && xchg(&rq->numa_migrate_on, 1)) return; /* @@ -1730,19 +1789,39 @@ static void task_numa_compare(struct task_numa_env *env, goto unlock; assign: - /* - * One idle CPU per node is evaluated for a task numa move. - * Call select_idle_sibling to maybe find a better one. - */ + /* Evaluate an idle CPU for a task numa move. */ if (!cur) { + int cpu = env->dst_stats.idle_cpu; + + /* Nothing cached so current CPU went idle since the search. */ + if (cpu < 0) + cpu = env->dst_cpu; + /* - * select_idle_siblings() uses an per-CPU cpumask that - * can be used from IRQ context. + * If the CPU is no longer truly idle and the previous best CPU + * is, keep using it. */ - local_irq_disable(); - env->dst_cpu = select_idle_sibling(env->p, env->src_cpu, + if (!idle_cpu(cpu) && env->best_cpu >= 0 && + idle_cpu(env->best_cpu)) { + cpu = env->best_cpu; + } + + /* + * Use select_idle_sibling if the previously found idle CPU is + * not idle any more. + */ + if (!idle_cpu(cpu)) { + /* + * select_idle_siblings() uses an per-CPU cpumask that + * can be used from IRQ context. + */ + local_irq_disable(); + cpu = select_idle_sibling(env->p, env->src_cpu, env->dst_cpu); - local_irq_enable(); + local_irq_enable(); + } + + env->dst_cpu = cpu; } task_numa_assign(env, cur, imp); @@ -1776,8 +1855,14 @@ static void task_numa_find_cpu(struct task_numa_env *env, imbalance = adjust_numa_imbalance(imbalance, src_running); /* Use idle CPU if there is no imbalance */ - if (!imbalance) + if (!imbalance) { maymove = true; + if (env->dst_stats.idle_cpu >= 0) { + env->dst_cpu = env->dst_stats.idle_cpu; + task_numa_assign(env, NULL, 0); + return; + } + } } else { long src_load, dst_load, load; /* @@ -1850,10 +1935,10 @@ static int task_numa_migrate(struct task_struct *p) dist = env.dist = node_distance(env.src_nid, env.dst_nid); taskweight = task_weight(p, env.src_nid, dist); groupweight = group_weight(p, env.src_nid, dist); - update_numa_stats(&env, &env.src_stats, env.src_nid); + update_numa_stats(&env, &env.src_stats, env.src_nid, false); taskimp = task_weight(p, env.dst_nid, dist) - taskweight; groupimp = group_weight(p, env.dst_nid, dist) - groupweight; - update_numa_stats(&env, &env.dst_stats, env.dst_nid); + update_numa_stats(&env, &env.dst_stats, env.dst_nid, true); /* Try to find a spot on the preferred nid. */ task_numa_find_cpu(&env, taskimp, groupimp); @@ -1886,7 +1971,7 @@ static int task_numa_migrate(struct task_struct *p) env.dist = dist; env.dst_nid = nid; - update_numa_stats(&env, &env.dst_stats, env.dst_nid); + update_numa_stats(&env, &env.dst_stats, env.dst_nid, true); task_numa_find_cpu(&env, taskimp, groupimp); } } From 5fb52dd93a2fe9a738f730de9da108bd1f6c30d0 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 24 Feb 2020 09:52:21 +0000 Subject: [PATCH 0556/2231] sched/numa: Find an alternative idle CPU if the CPU is part of an active NUMA balance Multiple tasks can attempt to select and idle CPU but fail because numa_migrate_on is already set and the migration fails. Instead of failing, scan for an alternative idle CPU. select_idle_sibling is not used because it requires IRQs to be disabled and it ignores numa_migrate_on allowing multiple tasks to stack. This scan may still fail if there are idle candidate CPUs due to races but if this occurs, it's best that a task stay on an available CPU that move to a contended one. Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Cc: Vincent Guittot Cc: Juri Lelli Cc: Dietmar Eggemann Cc: Steven Rostedt Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-12-mgorman@techsingularity.net --- kernel/sched/fair.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2da21f44e4d0..050c1b19bfc0 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1624,15 +1624,34 @@ static void task_numa_assign(struct task_numa_env *env, { struct rq *rq = cpu_rq(env->dst_cpu); - /* Bail out if run-queue part of active NUMA balance. */ - if (env->best_cpu != env->dst_cpu && xchg(&rq->numa_migrate_on, 1)) - return; + /* Check if run-queue part of active NUMA balance. */ + if (env->best_cpu != env->dst_cpu && xchg(&rq->numa_migrate_on, 1)) { + int cpu; + int start = env->dst_cpu; + /* Find alternative idle CPU. */ + for_each_cpu_wrap(cpu, cpumask_of_node(env->dst_nid), start) { + if (cpu == env->best_cpu || !idle_cpu(cpu) || + !cpumask_test_cpu(cpu, env->p->cpus_ptr)) { + continue; + } + + env->dst_cpu = cpu; + rq = cpu_rq(env->dst_cpu); + if (!xchg(&rq->numa_migrate_on, 1)) + goto assign; + } + + /* Failed to find an alternative idle CPU */ + return; + } + +assign: /* * Clear previous best_cpu/rq numa-migrate flag, since task now * found a better CPU to move/swap. */ - if (env->best_cpu != -1) { + if (env->best_cpu != -1 && env->best_cpu != env->dst_cpu) { rq = cpu_rq(env->best_cpu); WRITE_ONCE(rq->numa_migrate_on, 0); } @@ -1806,21 +1825,6 @@ assign: cpu = env->best_cpu; } - /* - * Use select_idle_sibling if the previously found idle CPU is - * not idle any more. - */ - if (!idle_cpu(cpu)) { - /* - * select_idle_siblings() uses an per-CPU cpumask that - * can be used from IRQ context. - */ - local_irq_disable(); - cpu = select_idle_sibling(env->p, env->src_cpu, - env->dst_cpu); - local_irq_enable(); - } - env->dst_cpu = cpu; } From 88cca72c9673e631b63eca7a1dba4a9722a3f414 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 24 Feb 2020 09:52:22 +0000 Subject: [PATCH 0557/2231] sched/numa: Bias swapping tasks based on their preferred node When swapping tasks for NUMA balancing, it is preferred that tasks move to or remain on their preferred node. When considering an imbalance, encourage tasks to move to their preferred node and discourage tasks from moving away from their preferred node. Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Cc: Vincent Guittot Cc: Juri Lelli Cc: Dietmar Eggemann Cc: Steven Rostedt Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-13-mgorman@techsingularity.net --- kernel/sched/fair.c | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 050c1b19bfc0..8c1ac01a10d5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1741,18 +1741,27 @@ static void task_numa_compare(struct task_numa_env *env, goto unlock; } + /* Skip this swap candidate if cannot move to the source cpu. */ + if (!cpumask_test_cpu(env->src_cpu, cur->cpus_ptr)) + goto unlock; + + /* + * Skip this swap candidate if it is not moving to its preferred + * node and the best task is. + */ + if (env->best_task && + env->best_task->numa_preferred_nid == env->src_nid && + cur->numa_preferred_nid != env->src_nid) { + goto unlock; + } + /* * "imp" is the fault differential for the source task between the * source and destination node. Calculate the total differential for * the source task and potential destination task. The more negative * the value is, the more remote accesses that would be expected to * be incurred if the tasks were swapped. - */ - /* Skip this swap candidate if cannot move to the source cpu */ - if (!cpumask_test_cpu(env->src_cpu, cur->cpus_ptr)) - goto unlock; - - /* + * * If dst and source tasks are in the same NUMA group, or not * in any group then look only at task weights. */ @@ -1779,12 +1788,34 @@ static void task_numa_compare(struct task_numa_env *env, task_weight(cur, env->dst_nid, dist); } + /* Discourage picking a task already on its preferred node */ + if (cur->numa_preferred_nid == env->dst_nid) + imp -= imp / 16; + + /* + * Encourage picking a task that moves to its preferred node. + * This potentially makes imp larger than it's maximum of + * 1998 (see SMALLIMP and task_weight for why) but in this + * case, it does not matter. + */ + if (cur->numa_preferred_nid == env->src_nid) + imp += imp / 8; + if (maymove && moveimp > imp && moveimp > env->best_imp) { imp = moveimp; cur = NULL; goto assign; } + /* + * Prefer swapping with a task moving to its preferred node over a + * task that is not. + */ + if (env->best_task && cur->numa_preferred_nid == env->src_nid && + env->best_task->numa_preferred_nid != env->src_nid) { + goto assign; + } + /* * If the NUMA importance is less than SMALLIMP, * task migration might only result in ping pong From a0f03b617c3b2644d3d47bf7d9e60aed01bd5b10 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 24 Feb 2020 09:52:23 +0000 Subject: [PATCH 0558/2231] sched/numa: Stop an exhastive search if a reasonable swap candidate or idle CPU is found When domains are imbalanced or overloaded a search of all CPUs on the target domain is searched and compared with task_numa_compare. In some circumstances, a candidate is found that is an obvious win. o A task can move to an idle CPU and an idle CPU is found o A swap candidate is found that would move to its preferred domain This patch terminates the search when either condition is met. Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Cc: Vincent Guittot Cc: Juri Lelli Cc: Dietmar Eggemann Cc: Steven Rostedt Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-14-mgorman@techsingularity.net --- kernel/sched/fair.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8c1ac01a10d5..fcc968669aea 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1707,7 +1707,7 @@ static bool load_too_imbalanced(long src_load, long dst_load, * into account that it might be best if task running on the dst_cpu should * be exchanged with the source task */ -static void task_numa_compare(struct task_numa_env *env, +static bool task_numa_compare(struct task_numa_env *env, long taskimp, long groupimp, bool maymove) { struct numa_group *cur_ng, *p_ng = deref_curr_numa_group(env->p); @@ -1718,9 +1718,10 @@ static void task_numa_compare(struct task_numa_env *env, int dist = env->dist; long moveimp = imp; long load; + bool stopsearch = false; if (READ_ONCE(dst_rq->numa_migrate_on)) - return; + return false; rcu_read_lock(); cur = rcu_dereference(dst_rq->curr); @@ -1731,8 +1732,10 @@ static void task_numa_compare(struct task_numa_env *env, * Because we have preemption enabled we can get migrated around and * end try selecting ourselves (current == env->p) as a swap candidate. */ - if (cur == env->p) + if (cur == env->p) { + stopsearch = true; goto unlock; + } if (!cur) { if (maymove && moveimp >= env->best_imp) @@ -1860,8 +1863,27 @@ assign: } task_numa_assign(env, cur, imp); + + /* + * If a move to idle is allowed because there is capacity or load + * balance improves then stop the search. While a better swap + * candidate may exist, a search is not free. + */ + if (maymove && !cur && env->best_cpu >= 0 && idle_cpu(env->best_cpu)) + stopsearch = true; + + /* + * If a swap candidate must be identified and the current best task + * moves its preferred node then stop the search. + */ + if (!maymove && env->best_task && + env->best_task->numa_preferred_nid == env->src_nid) { + stopsearch = true; + } unlock: rcu_read_unlock(); + + return stopsearch; } static void task_numa_find_cpu(struct task_numa_env *env, @@ -1916,7 +1938,8 @@ static void task_numa_find_cpu(struct task_numa_env *env, continue; env->dst_cpu = cpu; - task_numa_compare(env, taskimp, groupimp, maymove); + if (task_numa_compare(env, taskimp, groupimp, maymove)) + break; } } From a0fc8b6a3bc52f3a3ecf13eb380e15c470b8e6f8 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Sun, 23 Feb 2020 00:56:34 +0100 Subject: [PATCH 0559/2231] regulator: axp20x: Fix misleading use of negation It works incidentally, because AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN is non-zero, but the false branch value really should be just 0. Signed-off-by: Ondrej Jirman Acked-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20200222235634.243805-1-megous@megous.com Signed-off-by: Mark Brown --- drivers/regulator/axp20x-regulator.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 16f0c8570036..1e6eb5b1f8d8 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -381,8 +381,7 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK | AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK; enable = (ramp > 0) ? - AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : - !AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN; + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : 0; break; } @@ -393,8 +392,7 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK | AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK; enable = (ramp > 0) ? - AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : - !AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN; + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : 0; break; } From 1498dfba5127c10b9208eb451a8d143b93b37a76 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 04:19:37 +0100 Subject: [PATCH 0560/2231] media: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 6 +- drivers/media/platform/Kconfig | 74 +++++++++++------------ drivers/media/platform/exynos4-is/Kconfig | 2 +- drivers/media/radio/si470x/Kconfig | 4 +- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index b36a41332867..9dfea5c4b6ab 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -208,9 +208,9 @@ config MEDIA_SUBDRV_AUTOSELECT If unsure say Y. config MEDIA_HIDE_ANCILLARY_SUBDRV - bool - depends on MEDIA_SUBDRV_AUTOSELECT && !COMPILE_TEST && !EXPERT - default y + bool + depends on MEDIA_SUBDRV_AUTOSELECT && !COMPILE_TEST && !EXPERT + default y config MEDIA_ATTACH bool diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f65e98d3adf2..965b7b6bb557 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -610,49 +610,49 @@ config CEC_GPIO between compatible devices. config VIDEO_SAMSUNG_S5P_CEC - tristate "Samsung S5P CEC driver" - depends on ARCH_EXYNOS || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for Samsung S5P HDMI CEC interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. + tristate "Samsung S5P CEC driver" + depends on ARCH_EXYNOS || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for Samsung S5P HDMI CEC interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. config VIDEO_STI_HDMI_CEC - tristate "STMicroelectronics STiH4xx HDMI CEC driver" - depends on ARCH_STI || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for STIH4xx HDMI CEC interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. + tristate "STMicroelectronics STiH4xx HDMI CEC driver" + depends on ARCH_STI || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for STIH4xx HDMI CEC interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. config VIDEO_STM32_HDMI_CEC - tristate "STMicroelectronics STM32 HDMI CEC driver" - depends on ARCH_STM32 || COMPILE_TEST - select REGMAP - select REGMAP_MMIO - select CEC_CORE - help - This is a driver for STM32 interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. + tristate "STMicroelectronics STM32 HDMI CEC driver" + depends on ARCH_STM32 || COMPILE_TEST + select REGMAP + select REGMAP_MMIO + select CEC_CORE + help + This is a driver for STM32 interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. config VIDEO_TEGRA_HDMI_CEC - tristate "Tegra HDMI CEC driver" - depends on ARCH_TEGRA || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for the Tegra HDMI CEC interface. It uses the - generic CEC framework interface. - The CEC bus is present in the HDMI connector and enables communication - between compatible devices. + tristate "Tegra HDMI CEC driver" + depends on ARCH_TEGRA || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for the Tegra HDMI CEC interface. It uses the + generic CEC framework interface. + The CEC bus is present in the HDMI connector and enables communication + between compatible devices. config VIDEO_SECO_CEC tristate "SECO Boards HDMI CEC driver" diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 989cb34f19b1..be4effcbfe7b 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -13,7 +13,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS if VIDEO_SAMSUNG_EXYNOS4_IS config VIDEO_EXYNOS4_IS_COMMON - tristate + tristate config VIDEO_S5P_FIMC tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" diff --git a/drivers/media/radio/si470x/Kconfig b/drivers/media/radio/si470x/Kconfig index 537f8e1601f3..a1ba8bc54b62 100644 --- a/drivers/media/radio/si470x/Kconfig +++ b/drivers/media/radio/si470x/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config RADIO_SI470X - tristate "Silicon Labs Si470x FM Radio Receiver support" - depends on VIDEO_V4L2 + tristate "Silicon Labs Si470x FM Radio Receiver support" + depends on VIDEO_V4L2 help This is a driver for devices with the Silicon Labs SI470x chip (either via USB or I2C buses). From 9ac560d48d3caab7c6e8cd259520598631535271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Fri, 6 Dec 2019 17:39:31 +0100 Subject: [PATCH 0561/2231] media: rcar-vin: Handle special pixel formats in a switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before extending the special pixel format handling turn the multiple if statements into a switch. Suggested-by: Geert Uytterhoeven Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 5ff565e76bca..a76472c62cb1 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -73,11 +73,18 @@ const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, { int i; - if (vin->info->model == RCAR_M1 && pixelformat == V4L2_PIX_FMT_XBGR32) - return NULL; - - if (pixelformat == V4L2_PIX_FMT_NV12 && !vin->info->nv12) - return NULL; + switch (pixelformat) { + case V4L2_PIX_FMT_XBGR32: + if (vin->info->model == RCAR_M1) + return NULL; + break; + case V4L2_PIX_FMT_NV12: + if (!vin->info->nv12) + return NULL; + break; + default: + break; + } for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) if (rvin_formats[i].fourcc == pixelformat) From e18976eecabd252ca406ae2325e983f8eaa348cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Fri, 6 Dec 2019 17:39:32 +0100 Subject: [PATCH 0562/2231] media: rcar-vin: Limit NV12 availability to supported VIN channels only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When adding support for NV12 it was overlooked that the pixel format is only supported on some VIN channels. Fix this by adding a check to only accept NV12 on the supported channels 0, 1, 4, 5, 8, 9, 12 and 13. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-v4l2.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index a76472c62cb1..c60dcf444b02 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -79,7 +79,11 @@ const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, return NULL; break; case V4L2_PIX_FMT_NV12: - if (!vin->info->nv12) + /* + * If NV12 is supported it's only supported on channels 0, 1, 4, + * 5, 8, 9, 12 and 13. + */ + if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333)) return NULL; break; default: From 2e1b100cdceb41ad05edc467a43e5affd2c87b6d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 16 Dec 2019 16:51:43 +0100 Subject: [PATCH 0563/2231] media: v4l2-core: v4l2-i2c: convert to new API with ERRPTR Use the new APIs instead of the deprecated ones. Signed-off-by: Wolfram Sang Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-i2c.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-i2c.c b/drivers/media/v4l2-core/v4l2-i2c.c index 5bf99e7c0c09..b4acca75644b 100644 --- a/drivers/media/v4l2-core/v4l2-i2c.c +++ b/drivers/media/v4l2-core/v4l2-i2c.c @@ -74,10 +74,10 @@ struct v4l2_subdev /* Create the i2c client */ if (info->addr == 0 && probe_addrs) - client = i2c_new_probed_device(adapter, info, probe_addrs, - NULL); + client = i2c_new_scanned_device(adapter, info, probe_addrs, + NULL); else - client = i2c_new_device(adapter, info); + client = i2c_new_client_device(adapter, info); /* * Note: by loading the module first we are certain that c->driver @@ -88,7 +88,7 @@ struct v4l2_subdev * want to use the i2c device, so explicitly loading the module * is the best alternative. */ - if (!client || !client->dev.driver) + if (!i2c_client_has_driver(client)) goto error; /* Lock the module so we can safely get the v4l2_subdev pointer */ @@ -110,7 +110,7 @@ error: * If we have a client but no subdev, then something went wrong and * we must unregister the client. */ - if (client && !sd) + if (!IS_ERR(client) && !sd) i2c_unregister_device(client); return sd; } From 639c0a5b0503fb57127fa8972208d43020a9bcf6 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Thu, 19 Dec 2019 21:29:24 +0100 Subject: [PATCH 0564/2231] media: media/saa7146: fix incorrect assertion in saa7146_buffer_finish In saa7146_buffer_finish, the code for q->curr to be NULL is already present and asserting for NULL is unnecessary. This patch elimiates such a check. Signed-off-by: Aditya Pakki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index aabb830e7468..d7e83b55ddca 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -97,8 +97,6 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state); DEB_EE("q->curr:%p\n", q->curr); - BUG_ON(!q->curr); - /* finish current buffer */ if (NULL == q->curr) { DEB_D("aiii. no current buffer\n"); From e4183df77fe38b69ee0d05230d9157e13987e3c5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 1 Jan 2020 08:43:21 +0100 Subject: [PATCH 0565/2231] media: anysee: constify copied structure The anysee_tda18212_config and anysee_tda18212_config2 structures are only copied into other structures, so make them const. The opportunity for this change was found using Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/anysee.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index 0514e87405b6..89a1b204b90c 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -318,14 +318,14 @@ static struct tda10023_config anysee_tda10023_tda18212_config = { .deltaf = 0xba02, }; -static struct tda18212_config anysee_tda18212_config = { +static const struct tda18212_config anysee_tda18212_config = { .if_dvbt_6 = 4150, .if_dvbt_7 = 4150, .if_dvbt_8 = 4150, .if_dvbc = 5000, }; -static struct tda18212_config anysee_tda18212_config2 = { +static const struct tda18212_config anysee_tda18212_config2 = { .if_dvbt_6 = 3550, .if_dvbt_7 = 3700, .if_dvbt_8 = 4150, From 43c85616f642fdb6af33f3b7bcbb6e14bcfd74ee Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 1 Jan 2020 08:43:23 +0100 Subject: [PATCH 0566/2231] media: isif: constify copied structure The isif_config_defaults structure is only copied into another structure, so make it const. The opportunity for this change was found using Coccinelle. Signed-off-by: Julia Lawall Acked-by: Lad Prabhakar Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/isif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index b49378b18e5d..c98edb67cfb2 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -29,7 +29,7 @@ #include "ccdc_hw_device.h" /* Defaults for module configuration parameters */ -static struct isif_config_params_raw isif_config_defaults = { +static const struct isif_config_params_raw isif_config_defaults = { .linearize = { .en = 0, .corr_shft = ISIF_NO_SHIFT, From af5b333a793a93da3b2fbe887681fd32f8095645 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 1 Jan 2020 08:43:29 +0100 Subject: [PATCH 0567/2231] media: cx231xx: constify copied structure The hauppauge_955q_lgdt3306a_config structure is only copied into another structure, so make it const. The opportunity for this change was found using Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-dvb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index e205f7f0a56a..0037b4b1381e 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -147,7 +147,7 @@ static struct tda18271_config pv_tda18271_config = { .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; -static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { +static const struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { .qam_if_khz = 4000, .vsb_if_khz = 3250, .spectral_inversion = 1, From 05e2ebfa2bb02d40857b1b6718d91135ee2cd7f5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 7 Jan 2020 12:52:53 +0100 Subject: [PATCH 0568/2231] media: stm32-dcmi: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. By using dma_request_chan() directly the driver can support deferred probing against DMA. Signed-off-by: Peter Ujfalusi Acked-by: Hugues Fruchet Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/stm32/stm32-dcmi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 9392e3409fba..b63ff8d636ce 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1910,10 +1910,13 @@ static int dcmi_probe(struct platform_device *pdev) return PTR_ERR(mclk); } - chan = dma_request_slave_channel(&pdev->dev, "tx"); - if (!chan) { - dev_info(&pdev->dev, "Unable to request DMA channel, defer probing\n"); - return -EPROBE_DEFER; + chan = dma_request_chan(&pdev->dev, "tx"); + if (IS_ERR(chan)) { + ret = PTR_ERR(chan); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Failed to request DMA channel: %d\n", ret); + return ret; } spin_lock_init(&dcmi->irqlock); From 858eff03578c621728d219acfcc96b64938b0350 Mon Sep 17 00:00:00 2001 From: Hirokazu Honda Date: Fri, 22 Nov 2019 06:16:08 +0100 Subject: [PATCH 0569/2231] media: hantro: Support H264 profile control The Hantro G1 decoder supports H.264 profiles from Baseline to High, with the exception of the Extended profile. Expose the V4L2_CID_MPEG_VIDEO_H264_PROFILE control, so that the applications can query the driver for the list of supported profiles. Signed-off-by: Hirokazu Honda Reviewed-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_drv.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index c98835326135..ca8b133e2e46 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -361,6 +361,16 @@ static const struct hantro_ctrl controls[] = { .def = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, .max = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, }, + }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), + .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + } }, { }, }; From eee16d210c18577217789e17d36e1d38b5bc2237 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 17 Dec 2019 11:39:23 +0100 Subject: [PATCH 0570/2231] media: pxa_camera: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. By using dma_request_chan() directly the driver can support deferred probing against DMA. Signed-off-by: Peter Ujfalusi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/pxa_camera.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 43ae645d866b..321dee9ca81c 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2440,23 +2440,23 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->base = base; /* request dma */ - pcdev->dma_chans[0] = dma_request_slave_channel(&pdev->dev, "CI_Y"); - if (!pcdev->dma_chans[0]) { + pcdev->dma_chans[0] = dma_request_chan(&pdev->dev, "CI_Y"); + if (IS_ERR(pcdev->dma_chans[0])) { dev_err(&pdev->dev, "Can't request DMA for Y\n"); - return -ENODEV; + return PTR_ERR(pcdev->dma_chans[0]); } - pcdev->dma_chans[1] = dma_request_slave_channel(&pdev->dev, "CI_U"); - if (!pcdev->dma_chans[1]) { - dev_err(&pdev->dev, "Can't request DMA for Y\n"); - err = -ENODEV; + pcdev->dma_chans[1] = dma_request_chan(&pdev->dev, "CI_U"); + if (IS_ERR(pcdev->dma_chans[1])) { + dev_err(&pdev->dev, "Can't request DMA for U\n"); + err = PTR_ERR(pcdev->dma_chans[1]); goto exit_free_dma_y; } - pcdev->dma_chans[2] = dma_request_slave_channel(&pdev->dev, "CI_V"); - if (!pcdev->dma_chans[2]) { + pcdev->dma_chans[2] = dma_request_chan(&pdev->dev, "CI_V"); + if (IS_ERR(pcdev->dma_chans[2])) { dev_err(&pdev->dev, "Can't request DMA for V\n"); - err = -ENODEV; + err = PTR_ERR(pcdev->dma_chans[2]); goto exit_free_dma_u; } From 1b5482e0d2680da7202d29013f6b5aa5e203046f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 17 Dec 2019 11:40:25 +0100 Subject: [PATCH 0571/2231] media: rcar_drif: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. By using dma_request_chan() directly the driver can support deferred probing against DMA. Signed-off-by: Peter Ujfalusi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar_drif.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index 0f267a237b42..02ee0e2a09e9 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -275,10 +275,10 @@ static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr) for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { struct rcar_drif *ch = sdr->ch[i]; - ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx"); - if (!ch->dmach) { + ch->dmach = dma_request_chan(&ch->pdev->dev, "rx"); + if (IS_ERR(ch->dmach)) { rdrif_err(sdr, "ch%u: dma channel req failed\n", i); - ret = -ENODEV; + ret = PTR_ERR(ch->dmach); goto dmach_error; } From 297c5ac328066a753699b2beddcc31a98acb366e Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Tue, 7 Jan 2020 02:15:02 +0100 Subject: [PATCH 0572/2231] media: Documentation: dt-bindings: media: add AST2600 Video Engine support The AST2600 has Video Engine so add the compatible string into the document. Signed-off-by: Jae Hyun Yoo Acked-by: Joel Stanley Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/aspeed-video.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt b/Documentation/devicetree/bindings/media/aspeed-video.txt index ce2894506e1f..d2ca32512272 100644 --- a/Documentation/devicetree/bindings/media/aspeed-video.txt +++ b/Documentation/devicetree/bindings/media/aspeed-video.txt @@ -1,11 +1,12 @@ * Device tree bindings for Aspeed Video Engine -The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can +The Video Engine (VE) embedded in the Aspeed AST2400/2500/2600 SOCs can capture and compress video data from digital or analog sources. Required properties: - compatible: "aspeed,ast2400-video-engine" or - "aspeed,ast2500-video-engine" + "aspeed,ast2500-video-engine" or + "aspeed,ast2600-video-engine" - reg: contains the offset and length of the VE memory region - clocks: clock specifiers for the syscon clocks associated with the VE (ordering must match the clock-names property) From aef0a5090b9952878a7a3626e29cfeda7f68a51f Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 7 Jan 2020 04:43:22 +0100 Subject: [PATCH 0573/2231] media: aspeed: Rework memory mapping in probe Use the recently introduced function devm_platform_ioremap_resource to save a few lines of code. This makes the driver match common platform device probe patterns. Signed-off-by: Joel Stanley Reviewed-by: Jae Hyun Yoo Reviewed-by: Eddie James Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index d8593cb2ae84..8f849d9866af 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1655,14 +1655,17 @@ err_unprepare_eclk: static int aspeed_video_probe(struct platform_device *pdev) { + struct aspeed_video *video; int rc; - struct resource *res; - struct aspeed_video *video = - devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL); + video = devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL); if (!video) return -ENOMEM; + video->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(video->base)) + return PTR_ERR(video->base); + video->frame_rate = 30; video->dev = &pdev->dev; spin_lock_init(&video->lock); @@ -1671,13 +1674,6 @@ static int aspeed_video_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); INIT_LIST_HEAD(&video->buffers); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - video->base = devm_ioremap_resource(video->dev, res); - - if (IS_ERR(video->base)) - return PTR_ERR(video->base); - rc = aspeed_video_init(video); if (rc) return rc; From 39857de4f82f1458842edd7f3a9f2e82ab48b479 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 7 Jan 2020 04:43:23 +0100 Subject: [PATCH 0574/2231] media: aspeed: Use runtime configuration The aspeed video IP has some differences between SoC families. Currently the driver decides which registers to use at compile time, which means a single kernel can not be used between platforms. Switch to using runtime configuration of the registers that vary between SoC families. This is in preparation for upcoming ast2600 support. [hverkuil: replace ;; by ;] Signed-off-by: Joel Stanley Reviewed-by: Eddie James Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 53 ++++++++++++++++++++------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index 8f849d9866af..c55dfdb34a54 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -72,11 +72,8 @@ #define VE_SEQ_CTRL_CAP_BUSY BIT(16) #define VE_SEQ_CTRL_COMP_BUSY BIT(18) -#ifdef CONFIG_MACH_ASPEED_G5 -#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500 */ -#else -#define VE_SEQ_CTRL_JPEG_MODE BIT(8) /* AST2400 */ -#endif /* CONFIG_MACH_ASPEED_G5 */ +#define AST2500_VE_SEQ_CTRL_JPEG_MODE BIT(13) +#define AST2400_VE_SEQ_CTRL_JPEG_MODE BIT(8) #define VE_CTRL 0x008 #define VE_CTRL_HSYNC_POL BIT(0) @@ -220,6 +217,9 @@ struct aspeed_video { struct video_device vdev; struct mutex video_lock; /* v4l2 and videobuf2 lock */ + u32 jpeg_mode; + u32 comp_size_read; + wait_queue_head_t wait; spinlock_t lock; /* buffer list lock */ struct delayed_work res_work; @@ -243,6 +243,21 @@ struct aspeed_video { #define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev) +struct aspeed_video_config { + u32 jpeg_mode; + u32 comp_size_read; +}; + +static const struct aspeed_video_config ast2400_config = { + .jpeg_mode = AST2400_VE_SEQ_CTRL_JPEG_MODE, + .comp_size_read = VE_OFFSET_COMP_STREAM, +}; + +static const struct aspeed_video_config ast2500_config = { + .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, + .comp_size_read = VE_OFFSET_COMP_STREAM, +}; + static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = { 0xe0ffd8ff, 0x464a1000, 0x01004649, 0x60000101, 0x00006000, 0x0f00feff, 0x00002d05, 0x00000000, 0x00000000, 0x00dbff00 @@ -572,7 +587,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) if (sts & VE_INTERRUPT_COMP_COMPLETE) { struct aspeed_video_buffer *buf; u32 frame_size = aspeed_video_read(video, - VE_OFFSET_COMP_STREAM); + video->comp_size_read); spin_lock(&video->lock); clear_bit(VIDEO_FRAME_INPRG, &video->flags); @@ -907,7 +922,7 @@ static void aspeed_video_init_regs(struct aspeed_video *video) FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); u32 ctrl = VE_CTRL_AUTO_OR_CURSOR; - u32 seq_ctrl = VE_SEQ_CTRL_JPEG_MODE; + u32 seq_ctrl = video->jpeg_mode; if (video->frame_rate) ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate); @@ -1653,8 +1668,17 @@ err_unprepare_eclk: return rc; } +static const struct of_device_id aspeed_video_of_match[] = { + { .compatible = "aspeed,ast2400-video-engine", .data = &ast2400_config }, + { .compatible = "aspeed,ast2500-video-engine", .data = &ast2500_config }, + {} +}; +MODULE_DEVICE_TABLE(of, aspeed_video_of_match); + static int aspeed_video_probe(struct platform_device *pdev) { + const struct aspeed_video_config *config; + const struct of_device_id *match; struct aspeed_video *video; int rc; @@ -1666,6 +1690,14 @@ static int aspeed_video_probe(struct platform_device *pdev) if (IS_ERR(video->base)) return PTR_ERR(video->base); + match = of_match_node(aspeed_video_of_match, pdev->dev.of_node); + if (!match) + return -EINVAL; + + config = match->data; + video->jpeg_mode = config->jpeg_mode; + video->comp_size_read = config->comp_size_read; + video->frame_rate = 30; video->dev = &pdev->dev; spin_lock_init(&video->lock); @@ -1712,13 +1744,6 @@ static int aspeed_video_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id aspeed_video_of_match[] = { - { .compatible = "aspeed,ast2400-video-engine" }, - { .compatible = "aspeed,ast2500-video-engine" }, - {} -}; -MODULE_DEVICE_TABLE(of, aspeed_video_of_match); - static struct platform_driver aspeed_video_driver = { .driver = { .name = DEVICE_NAME, From bfaaa20df9f5e70ef6c4e610ce563ee7ea40b530 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 7 Jan 2020 04:43:24 +0100 Subject: [PATCH 0575/2231] media: video: aspeed: Update copyright information Use the preferred form for GPL2+, and add IBM and Intel as the authors. The driver was initially upstreamed by Eddie from IBM, and has had major contributions from Jae at Intel. Signed-off-by: Joel Stanley Acked-by: Jae Hyun Yoo Reviewed-by: Eddie James Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/aspeed-video.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index c55dfdb34a54..ef9c09298461 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1,4 +1,6 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2020 IBM Corp. +// Copyright (c) 2019 Intel Corporation #include #include From 4e52889f48fedfa5f2e443dc8490550cb49fbdc3 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Mon, 13 Jan 2020 10:48:53 +0100 Subject: [PATCH 0576/2231] media: atmel: atmel-isc-base: expose white balance as v4l2 controls This exposes the white balance configuration of the ISC as v4l2 controls into userspace. There are 8 controls available: 4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB. These gains are multipliers for each component, in format unsigned 0:4:9 with a default value of 512 (1.0 multiplier). 4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB. These offsets are added/substracted from each component, in format signed 1:12:0 with a default value of 0 (+/- 0) To expose this to userspace, added 8 custom controls, in an auto cluster. To summarize the functionality: The auto cluster switch is the auto white balance control, and it works like this: AWB == 1: autowhitebalance is on, the do_white_balance button is inactive, the gains/offsets are inactive, but volatile and readable. Thus, the results of the whitebalance algorithm are available to userspace to read at any time. AWB == 0: autowhitebalance is off, cluster is in manual mode, user can configure the gain/offsets directly. More than that, if the do_white_balance button is pressed, the driver will perform one-time-adjustment, (preferably with color checker card) and the userspace can read again the new values. With this feature, the userspace can save the coefficients and reinstall them for example after reboot or reprobing the driver. [hverkuil: fix checkpatch warning] [hverkuil: minor spacing adjustments in the functionality description] Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isc-base.c | 246 +++++++++++++++--- drivers/media/platform/atmel/atmel-isc.h | 23 +- include/linux/atmel-isc-media.h | 58 +++++ include/uapi/linux/v4l2-controls.h | 6 + 4 files changed, 293 insertions(+), 40 deletions(-) create mode 100644 include/linux/atmel-isc-media.h diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index d7669a03e98e..b6c9a78c9198 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -224,10 +225,35 @@ const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) +#define ISC_CTRL_ISC_TO_V4L2(x) ((x) == ISC_WB_O_ZERO_VAL ? 0 : (x)) +#define ISC_CTRL_V4L2_TO_ISC(x) ((x) ? (x) : ISC_WB_O_ZERO_VAL) + +static inline void isc_update_v4l2_ctrls(struct isc_device *isc) +{ + struct isc_ctrls *ctrls = &isc->ctrls; + + /* In here we set the v4l2 controls w.r.t. our pipeline config */ + v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); + v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); + v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); + v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); + + v4l2_ctrl_s_ctrl(isc->r_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R])); + v4l2_ctrl_s_ctrl(isc->b_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B])); + v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR])); + v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB])); +} + static inline void isc_update_awb_ctrls(struct isc_device *isc) { struct isc_ctrls *ctrls = &isc->ctrls; + /* In here we set our actual hw pipeline config */ + regmap_write(isc->regmap, ISC_WB_O_RGR, (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); @@ -662,11 +688,9 @@ static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) bay_cfg = isc->config.sd_format->cfa_baycfg; - if (ctrls->awb == ISC_WB_NONE) - isc_reset_awb_ctrls(isc); - regmap_write(regmap, ISC_WB_CFG, bay_cfg); isc_update_awb_ctrls(isc); + isc_update_v4l2_ctrls(isc); regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); @@ -1396,6 +1420,7 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) isc->try_config.sd_format != isc->config.sd_format) { isc->ctrls.hist_stat = HIST_INIT; isc_reset_awb_ctrls(isc); + isc_update_v4l2_ctrls(isc); } /* make the try configuration active */ isc->config = isc->try_config; @@ -1814,10 +1839,6 @@ static void isc_awb_work(struct work_struct *w) ctrls->hist_id = hist_id; baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; - /* if no more auto white balance, reset controls. */ - if (ctrls->awb == ISC_WB_NONE) - isc_reset_awb_ctrls(isc); - pm_runtime_get_sync(isc->dev); /* @@ -1842,6 +1863,8 @@ static void isc_awb_work(struct work_struct *w) if (ctrls->awb == ISC_WB_ONETIME) { v4l2_info(&isc->v4l2_dev, "Completed one time white-balance adjustment.\n"); + /* update the v4l2 controls values */ + isc_update_v4l2_ctrls(isc); ctrls->awb = ISC_WB_NONE; } } @@ -1873,35 +1896,6 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_GAMMA: ctrls->gamma_index = ctrl->val; break; - case V4L2_CID_AUTO_WHITE_BALANCE: - if (ctrl->val == 1) - ctrls->awb = ISC_WB_AUTO; - else - ctrls->awb = ISC_WB_NONE; - - /* we did not configure ISC yet */ - if (!isc->config.sd_format) - break; - - if (ctrls->hist_stat != HIST_ENABLED) - isc_reset_awb_ctrls(isc); - - if (isc->ctrls.awb == ISC_WB_AUTO && - vb2_is_streaming(&isc->vb2_vidq) && - ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) - isc_set_histogram(isc, true); - - break; - case V4L2_CID_DO_WHITE_BALANCE: - /* if AWB is enabled, do nothing */ - if (ctrls->awb == ISC_WB_AUTO) - return 0; - - ctrls->awb = ISC_WB_ONETIME; - isc_set_histogram(isc, true); - v4l2_dbg(1, debug, &isc->v4l2_dev, - "One time white-balance started.\n"); - break; default: return -EINVAL; } @@ -1913,6 +1907,162 @@ static const struct v4l2_ctrl_ops isc_ctrl_ops = { .s_ctrl = isc_s_ctrl, }; +static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + if (ctrl->val == 1) + ctrls->awb = ISC_WB_AUTO; + else + ctrls->awb = ISC_WB_NONE; + + /* we did not configure ISC yet */ + if (!isc->config.sd_format) + break; + + /* configure the controls with new values from v4l2 */ + if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; + + if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_R] = + ISC_CTRL_V4L2_TO_ISC(isc->r_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_B] = + ISC_CTRL_V4L2_TO_ISC(isc->b_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_GR] = + ISC_CTRL_V4L2_TO_ISC(isc->gr_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_GB] = + ISC_CTRL_V4L2_TO_ISC(isc->gb_off_ctrl->val); + + isc_update_awb_ctrls(isc); + + if (vb2_is_streaming(&isc->vb2_vidq)) { + /* + * If we are streaming, we can update profile to + * have the new settings in place. + */ + isc_update_profile(isc); + } else { + /* + * The auto cluster will activate automatically this + * control. This has to be deactivated when not + * streaming. + */ + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + } + + /* if we have autowhitebalance on, start histogram procedure */ + if (ctrls->awb == ISC_WB_AUTO && + vb2_is_streaming(&isc->vb2_vidq) && + ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) + isc_set_histogram(isc, true); + + /* + * for one time whitebalance adjustment, check the button, + * if it's pressed, perform the one time operation. + */ + if (ctrls->awb == ISC_WB_NONE && + ctrl->cluster[ISC_CTRL_DO_WB]->is_new && + !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & + V4L2_CTRL_FLAG_INACTIVE)) { + ctrls->awb = ISC_WB_ONETIME; + isc_set_histogram(isc, true); + v4l2_dbg(1, debug, &isc->v4l2_dev, + "One time white-balance started.\n"); + } + return 0; + } + return 0; +} + +static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + switch (ctrl->id) { + /* being a cluster, this id will be called for every control */ + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->cluster[ISC_CTRL_R_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_R]; + ctrl->cluster[ISC_CTRL_B_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_B]; + ctrl->cluster[ISC_CTRL_GR_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_GR]; + ctrl->cluster[ISC_CTRL_GB_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_GB]; + + ctrl->cluster[ISC_CTRL_R_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R]); + ctrl->cluster[ISC_CTRL_B_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B]); + ctrl->cluster[ISC_CTRL_GR_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR]); + ctrl->cluster[ISC_CTRL_GB_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB]); + break; + } + return 0; +} + +static const struct v4l2_ctrl_ops isc_awb_ops = { + .s_ctrl = isc_s_awb_ctrl, + .g_volatile_ctrl = isc_g_volatile_awb_ctrl, +}; + +#define ISC_CTRL_OFF(_name, _id, _name_str) \ + static const struct v4l2_ctrl_config _name = { \ + .ops = &isc_awb_ops, \ + .id = _id, \ + .name = _name_str, \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .flags = V4L2_CTRL_FLAG_SLIDER, \ + .min = -4095, \ + .max = 4095, \ + .step = 1, \ + .def = 0, \ + } + +ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); +ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); +ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); +ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); + +#define ISC_CTRL_GAIN(_name, _id, _name_str) \ + static const struct v4l2_ctrl_config _name = { \ + .ops = &isc_awb_ops, \ + .id = _id, \ + .name = _name_str, \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .flags = V4L2_CTRL_FLAG_SLIDER, \ + .min = 0, \ + .max = 8191, \ + .step = 1, \ + .def = 512, \ + } + +ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain"); +ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain"); +ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain"); +ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain"); + static int isc_ctrl_init(struct isc_device *isc) { const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; @@ -1923,7 +2073,7 @@ static int isc_ctrl_init(struct isc_device *isc) ctrls->hist_stat = HIST_INIT; isc_reset_awb_ctrls(isc); - ret = v4l2_ctrl_handler_init(hdl, 5); + ret = v4l2_ctrl_handler_init(hdl, 13); if (ret < 0) return ret; @@ -1933,10 +2083,13 @@ static int isc_ctrl_init(struct isc_device *isc) v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 2); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); /* do_white_balance is a button, so min,max,step,default are ignored */ - isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DO_WHITE_BALANCE, + isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, + V4L2_CID_DO_WHITE_BALANCE, 0, 0, 0, 0); if (!isc->do_wb_ctrl) { @@ -1947,6 +2100,21 @@ static int isc_ctrl_init(struct isc_device *isc) v4l2_ctrl_activate(isc->do_wb_ctrl, false); + isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL); + isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL); + isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL); + isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL); + isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); + isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); + isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); + isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); + + /* + * The cluster is in auto mode with autowhitebalance enabled + * and manual mode otherwise. + */ + v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); + v4l2_ctrl_handler_setup(hdl); return 0; diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index bfaed2fad2b5..fc56a745c7d1 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -213,7 +213,6 @@ struct isc_device { struct fmt_config try_config; struct isc_ctrls ctrls; - struct v4l2_ctrl *do_wb_ctrl; struct work_struct awb_work; struct mutex lock; /* serialize access to file operations */ @@ -223,6 +222,28 @@ struct isc_device { struct isc_subdev_entity *current_subdev; struct list_head subdev_entities; + + struct { +#define ISC_CTRL_DO_WB 1 +#define ISC_CTRL_R_GAIN 2 +#define ISC_CTRL_B_GAIN 3 +#define ISC_CTRL_GR_GAIN 4 +#define ISC_CTRL_GB_GAIN 5 +#define ISC_CTRL_R_OFF 6 +#define ISC_CTRL_B_OFF 7 +#define ISC_CTRL_GR_OFF 8 +#define ISC_CTRL_GB_OFF 9 + struct v4l2_ctrl *awb_ctrl; + struct v4l2_ctrl *do_wb_ctrl; + struct v4l2_ctrl *r_gain_ctrl; + struct v4l2_ctrl *b_gain_ctrl; + struct v4l2_ctrl *gr_gain_ctrl; + struct v4l2_ctrl *gb_gain_ctrl; + struct v4l2_ctrl *r_off_ctrl; + struct v4l2_ctrl *b_off_ctrl; + struct v4l2_ctrl *gr_off_ctrl; + struct v4l2_ctrl *gb_off_ctrl; + }; }; #define GAMMA_MAX 2 diff --git a/include/linux/atmel-isc-media.h b/include/linux/atmel-isc-media.h new file mode 100644 index 000000000000..79a320fb724e --- /dev/null +++ b/include/linux/atmel-isc-media.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019 Microchip Technology Inc. and its subsidiaries + * + * Author: Eugen Hristev + */ + +#ifndef __LINUX_ATMEL_ISC_MEDIA_H__ +#define __LINUX_ATMEL_ISC_MEDIA_H__ + +/* + * There are 8 controls available: + * 4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB. + * These gains are multipliers for each component, in format unsigned 0:4:9 with + * a default value of 512 (1.0 multiplier). + * 4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB. + * These offsets are added/substracted from each component, in format signed + * 1:12:0 with a default value of 0 (+/- 0) + * + * To expose this to userspace, added 8 custom controls, in an auto cluster. + * + * To summarize the functionality: + * The auto cluster switch is the auto white balance control, and it works + * like this: + * AWB == 1: autowhitebalance is on, the do_white_balance button is inactive, + * the gains/offsets are inactive, but volatile and readable. + * Thus, the results of the whitebalance algorithm are available to userspace to + * read at any time. + * AWB == 0: autowhitebalance is off, cluster is in manual mode, user can + * configure the gain/offsets directly. + * More than that, if the do_white_balance button is + * pressed, the driver will perform one-time-adjustment, (preferably with color + * checker card) and the userspace can read again the new values. + * + * With this feature, the userspace can save the coefficients and reinstall them + * for example after reboot or reprobing the driver. + */ + +enum atmel_isc_ctrl_id { + /* Red component gain control */ + ISC_CID_R_GAIN = (V4L2_CID_USER_ATMEL_ISC_BASE + 0), + /* Blue component gain control */ + ISC_CID_B_GAIN, + /* Green Red component gain control */ + ISC_CID_GR_GAIN, + /* Green Blue gain control */ + ISC_CID_GB_GAIN, + /* Red component offset control */ + ISC_CID_R_OFFSET, + /* Blue component offset control */ + ISC_CID_B_OFFSET, + /* Green Red component offset control */ + ISC_CID_GR_OFFSET, + /* Green Blue component offset control */ + ISC_CID_GB_OFFSET, +}; + +#endif diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 5a7bedee2b0e..1a58d7cc4ccc 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -192,6 +192,12 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x10b0) +/* + * The base for the atmel isc driver controls. + * We reserve 32 controls for this driver. + */ +#define V4L2_CID_USER_ATMEL_ISC_BASE (V4L2_CID_USER_BASE + 0x10c0) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ From b36be08f204f1e0b40860a253a1515df60c2a83c Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Mon, 13 Jan 2020 10:48:54 +0100 Subject: [PATCH 0577/2231] media: MAINTAINERS: add atmel-isc-media.h file to ATMEL ISC driver Add new file include/linux/atmel-isc-media.h to ATMEL ISC entry. Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 38fe2f3f7b6f..fe3ab10354c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10944,6 +10944,7 @@ F: drivers/media/platform/atmel/atmel-isc.h F: drivers/media/platform/atmel/atmel-isc-base.c F: drivers/media/platform/atmel/atmel-isc-regs.h F: Documentation/devicetree/bindings/media/atmel-isc.txt +F: include/linux/atmel-isc-media.h MICROCHIP ISI DRIVER M: Eugen Hristev From 536f561d871c5781bc33d26d415685211b94032e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 13 Jan 2020 18:18:18 +0100 Subject: [PATCH 0578/2231] media: usbtv: fix control-message timeouts The driver was issuing synchronous uninterruptible control requests without using a timeout. This could lead to the driver hanging on various user requests due to a malfunctioning (or malicious) device until the device is physically disconnected. The USB upper limit of five seconds per request should be more than enough. Fixes: f3d27f34fdd7 ("[media] usbtv: Add driver for Fushicai USBTV007 video frame grabber") Fixes: c53a846c48f2 ("[media] usbtv: add video controls") Cc: stable # 3.11 Signed-off-by: Johan Hovold Acked-by: Lubomir Rintel Reviewed-by: Greg Kroah-Hartman Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbtv/usbtv-core.c | 2 +- drivers/media/usb/usbtv/usbtv-video.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 5095c380b2c1..ee9c656d121f 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -56,7 +56,7 @@ int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size) ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 0); + value, index, NULL, 0, USB_CTRL_GET_TIMEOUT); if (ret < 0) return ret; } diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 3d9284a09ee5..b249f037900c 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -800,7 +800,8 @@ static int usbtv_s_ctrl(struct v4l2_ctrl *ctrl) ret = usb_control_msg(usbtv->udev, usb_rcvctrlpipe(usbtv->udev, 0), USBTV_CONTROL_REG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, USBTV_BASE + 0x0244, (void *)data, 3, 0); + 0, USBTV_BASE + 0x0244, (void *)data, 3, + USB_CTRL_GET_TIMEOUT); if (ret < 0) goto error; } @@ -851,7 +852,7 @@ static int usbtv_s_ctrl(struct v4l2_ctrl *ctrl) ret = usb_control_msg(usbtv->udev, usb_sndctrlpipe(usbtv->udev, 0), USBTV_CONTROL_REG, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, (void *)data, size, 0); + 0, index, (void *)data, size, USB_CTRL_SET_TIMEOUT); error: if (ret < 0) From 25f98947ced09b3528f52c983960847a31a0b014 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 15 Jan 2020 18:32:54 +0100 Subject: [PATCH 0579/2231] media: vidioc-queryctrl.rst: fix error code An attempt to get a BUTTON control returns EACCES, not EINVAL. BUTTON controls have V4L2_CTRL_FLAG_WRITE_ONLY set, and the documentation for that flag correctly says that getting a write-only control returns EACCES. It's the description of the BUTTON type that's wrong, so fix that so they are consistent. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/vidioc-queryctrl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst index 6690928e657b..22ff3c6abd9c 100644 --- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst +++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst @@ -378,7 +378,7 @@ See also the examples in :ref:`control`. - 0 - 0 - A control which performs an action when set. Drivers must ignore - the value passed with ``VIDIOC_S_CTRL`` and return an ``EINVAL`` error + the value passed with ``VIDIOC_S_CTRL`` and return an ``EACCES`` error code on a ``VIDIOC_G_CTRL`` attempt. * - ``V4L2_CTRL_TYPE_INTEGER64`` - any From 23c72dd9ffd22d8a17ec1b432eafa4caf3d8ccb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Wed, 15 Jan 2020 20:27:19 +0100 Subject: [PATCH 0580/2231] media: adv7180: Add init_cfg pad operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a init_cfg pad operation so that configurations allocated with v4l2_subdev_alloc_pad_config() are initialized. Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 6528e2343fc8..00159daa6fcd 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -749,6 +749,17 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, return ret; } +static int adv7180_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { + .which = cfg ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + return adv7180_set_pad_format(sd, cfg, &fmt); +} + static int adv7180_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { @@ -854,6 +865,7 @@ static const struct v4l2_subdev_core_ops adv7180_core_ops = { }; static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { + .init_cfg = adv7180_init_cfg, .enum_mbus_code = adv7180_enum_mbus_code, .set_fmt = adv7180_set_pad_format, .get_fmt = adv7180_get_pad_format, From ceeb2e6166dddf3c9757abbbf84032027e2fa2d2 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 13 Jan 2020 19:59:33 +0100 Subject: [PATCH 0581/2231] media: vimc: streamer: fix memory leak in vimc subdevs if kthread_run fails In case kthread_run fails, the vimc subdevices should be notified that streaming stopped so they can release the memory for the streaming. Also, kthread should be set to NULL. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-streamer.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c index cd6b55433c9e..43e494df61d8 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -207,8 +207,13 @@ int vimc_streamer_s_stream(struct vimc_stream *stream, stream->kthread = kthread_run(vimc_streamer_thread, stream, "vimc-streamer thread"); - if (IS_ERR(stream->kthread)) - return PTR_ERR(stream->kthread); + if (IS_ERR(stream->kthread)) { + ret = PTR_ERR(stream->kthread); + dev_err(ved->dev, "kthread_run failed with %d\n", ret); + vimc_streamer_pipeline_terminate(stream); + stream->kthread = NULL; + return ret; + } } else { if (!stream->kthread) From dea0ab3782e302ed2c19522f753bbe2ccf40cbbb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 10 Jan 2020 08:16:48 +0100 Subject: [PATCH 0582/2231] media: xilinx: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. By using dma_request_chan() directly the driver can support deferred probing against DMA. Signed-off-by: Peter Ujfalusi Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/xilinx/xilinx-dma.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index b211380a11f2..3bb54a4db6a4 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -725,10 +725,11 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, /* ... and the DMA channel. */ snprintf(name, sizeof(name), "port%u", port); - dma->dma = dma_request_slave_channel(dma->xdev->dev, name); - if (dma->dma == NULL) { - dev_err(dma->xdev->dev, "no VDMA channel found\n"); - ret = -ENODEV; + dma->dma = dma_request_chan(dma->xdev->dev, name); + if (IS_ERR(dma->dma)) { + ret = PTR_ERR(dma->dma); + if (ret != -EPROBE_DEFER) + dev_err(dma->xdev->dev, "no VDMA channel found\n"); goto error; } @@ -752,7 +753,7 @@ void xvip_dma_cleanup(struct xvip_dma *dma) if (video_is_registered(&dma->video)) video_unregister_device(&dma->video); - if (dma->dma) + if (!IS_ERR_OR_NULL(dma->dma)) dma_release_channel(dma->dma); media_entity_cleanup(&dma->video.entity); From e72b7359be18b9c488ed51a653cd51dfa793b67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Tue, 10 Dec 2019 03:05:58 +0100 Subject: [PATCH 0583/2231] media: rcar-vin: Move hardware buffer tracking to own struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support SEQ_TB/BT not all buffers given to the hardware will be equal, the driver needs to keep track of different buffer types. Move the tracking of buffers given to hardware into a struct so additional tracking fields can be associated with each buffer. [hverkuil: fix small checkpatch warning] Signed-off-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 27 +++++++++++----------- drivers/media/platform/rcar-vin/rcar-vin.h | 9 ++++---- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index cf9029efeb04..62dee0777371 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -844,20 +844,20 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) dma_addr_t phys_addr; /* A already populated slot shall never be overwritten. */ - if (WARN_ON(vin->queue_buf[slot] != NULL)) + if (WARN_ON(vin->buf_hw[slot].buffer)) return; vin_dbg(vin, "Filling HW slot: %d\n", slot); if (list_empty(&vin->buf_list)) { - vin->queue_buf[slot] = NULL; + vin->buf_hw[slot].buffer = NULL; phys_addr = vin->scratch_phys; } else { /* Keep track of buffer we give to HW */ buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); vbuf = &buf->vb; list_del_init(to_buf_list(vbuf)); - vin->queue_buf[slot] = vbuf; + vin->buf_hw[slot].buffer = vbuf; /* Setup DMA */ phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); @@ -953,13 +953,14 @@ static irqreturn_t rvin_irq(int irq, void *data) } /* Capture frame */ - if (vin->queue_buf[slot]) { - vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms); - vin->queue_buf[slot]->sequence = vin->sequence; - vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); - vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, + if (vin->buf_hw[slot].buffer) { + vin->buf_hw[slot].buffer->field = + rvin_get_active_field(vin, vnms); + vin->buf_hw[slot].buffer->sequence = vin->sequence; + vin->buf_hw[slot].buffer->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vin->buf_hw[slot].buffer->vb2_buf, VB2_BUF_STATE_DONE); - vin->queue_buf[slot] = NULL; + vin->buf_hw[slot].buffer = NULL; } else { /* Scratch buffer was used, dropping frame. */ vin_dbg(vin, "Dropping frame %u\n", vin->sequence); @@ -983,10 +984,10 @@ static void return_all_buffers(struct rvin_dev *vin, int i; for (i = 0; i < HW_BUFFER_NUM; i++) { - if (vin->queue_buf[i]) { - vb2_buffer_done(&vin->queue_buf[i]->vb2_buf, + if (vin->buf_hw[i].buffer) { + vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf, state); - vin->queue_buf[i] = NULL; + vin->buf_hw[i].buffer = NULL; } } @@ -1291,7 +1292,7 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) vin->state = STOPPED; for (i = 0; i < HW_BUFFER_NUM; i++) - vin->queue_buf[i] = NULL; + vin->buf_hw[i].buffer = NULL; /* buffer queue */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index a36b0824f81d..0aa904a4af5b 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -164,9 +164,8 @@ struct rvin_info { * @scratch: cpu address for scratch buffer * @scratch_phys: physical address of the scratch buffer * - * @qlock: protects @queue_buf, @buf_list, @sequence - * @state - * @queue_buf: Keeps track of buffers given to HW slot + * @qlock: protects @buf_hw, @buf_list, @sequence and @state + * @buf_hw: Keeps track of buffers given to HW slot * @buf_list: list of queued buffers * @sequence: V4L2 buffers sequence number * @state: keeps track of operation state @@ -205,7 +204,9 @@ struct rvin_dev { dma_addr_t scratch_phys; spinlock_t qlock; - struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM]; + struct { + struct vb2_v4l2_buffer *buffer; + } buf_hw[HW_BUFFER_NUM]; struct list_head buf_list; unsigned int sequence; enum rvin_dma_state state; From 7e0cfdadd1aafdb6963291b65358e60a7af3a4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Tue, 10 Dec 2019 03:05:59 +0100 Subject: [PATCH 0584/2231] media: rcar-vin: Add support for V4L2_FIELD_SEQ_{TB,BT} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hardware does not support capturing the field types V4L2_FIELD_SEQ_TB and V4L2_FIELD_SEQ_BT. To capture in these formats the driver needs to adjust the offset of the capture buffer and capture twice to each vb2 buffer. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/rcar-vin/rcar-dma.c | 68 ++++++++++++++++++--- drivers/media/platform/rcar-vin/rcar-v4l2.c | 5 ++ drivers/media/platform/rcar-vin/rcar-vin.h | 19 ++++++ 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 62dee0777371..1a30cd036371 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -535,7 +535,7 @@ static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin) /* Set scaling coefficient */ crop_height = vin->crop.height; - if (V4L2_FIELD_IS_INTERLACED(vin->format.field)) + if (V4L2_FIELD_HAS_BOTH(vin->format.field)) crop_height *= 2; ys = 0; @@ -564,7 +564,7 @@ static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin) rvin_write(vin, 0, VNSLPOC_REG); rvin_write(vin, vin->format.width - 1, VNEPPOC_REG); - if (V4L2_FIELD_IS_INTERLACED(vin->format.field)) + if (V4L2_FIELD_HAS_BOTH(vin->format.field)) rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG); else rvin_write(vin, vin->format.height - 1, VNELPOC_REG); @@ -626,6 +626,8 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_FIELD_INTERLACED_BT: vnmc = VNMC_IM_FULL | VNMC_FOC; break; + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: case V4L2_FIELD_NONE: vnmc = VNMC_IM_ODD_EVEN; progressive = true; @@ -842,15 +844,32 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) struct rvin_buffer *buf; struct vb2_v4l2_buffer *vbuf; dma_addr_t phys_addr; + int prev; /* A already populated slot shall never be overwritten. */ if (WARN_ON(vin->buf_hw[slot].buffer)) return; - vin_dbg(vin, "Filling HW slot: %d\n", slot); + prev = (slot == 0 ? HW_BUFFER_NUM : slot) - 1; - if (list_empty(&vin->buf_list)) { + if (vin->buf_hw[prev].type == HALF_TOP) { + vbuf = vin->buf_hw[prev].buffer; + vin->buf_hw[slot].buffer = vbuf; + vin->buf_hw[slot].type = HALF_BOTTOM; + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + phys_addr = vin->buf_hw[prev].phys + + vin->format.sizeimage / 4; + break; + default: + phys_addr = vin->buf_hw[prev].phys + + vin->format.sizeimage / 2; + break; + } + } else if (list_empty(&vin->buf_list)) { vin->buf_hw[slot].buffer = NULL; + vin->buf_hw[slot].type = FULL; phys_addr = vin->scratch_phys; } else { /* Keep track of buffer we give to HW */ @@ -859,10 +878,18 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) list_del_init(to_buf_list(vbuf)); vin->buf_hw[slot].buffer = vbuf; + vin->buf_hw[slot].type = + V4L2_FIELD_IS_SEQUENTIAL(vin->format.field) ? + HALF_TOP : FULL; + /* Setup DMA */ phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); } + vin_dbg(vin, "Filling HW slot: %d type: %d buffer: %p\n", + slot, vin->buf_hw[slot].type, vin->buf_hw[slot].buffer); + + vin->buf_hw[slot].phys = phys_addr; rvin_set_slot_addr(vin, slot, phys_addr); } @@ -870,6 +897,11 @@ static int rvin_capture_start(struct rvin_dev *vin) { int slot, ret; + for (slot = 0; slot < HW_BUFFER_NUM; slot++) { + vin->buf_hw[slot].buffer = NULL; + vin->buf_hw[slot].type = FULL; + } + for (slot = 0; slot < HW_BUFFER_NUM; slot++) rvin_fill_hw_slot(vin, slot); @@ -954,6 +986,16 @@ static irqreturn_t rvin_irq(int irq, void *data) /* Capture frame */ if (vin->buf_hw[slot].buffer) { + /* + * Nothing to do but refill the hardware slot if + * capture only filled first half of vb2 buffer. + */ + if (vin->buf_hw[slot].type == HALF_TOP) { + vin->buf_hw[slot].buffer = NULL; + rvin_fill_hw_slot(vin, slot); + goto done; + } + vin->buf_hw[slot].buffer->field = rvin_get_active_field(vin, vnms); vin->buf_hw[slot].buffer->sequence = vin->sequence; @@ -981,14 +1023,22 @@ static void return_all_buffers(struct rvin_dev *vin, enum vb2_buffer_state state) { struct rvin_buffer *buf, *node; - int i; + struct vb2_v4l2_buffer *freed[HW_BUFFER_NUM]; + unsigned int i, n; for (i = 0; i < HW_BUFFER_NUM; i++) { - if (vin->buf_hw[i].buffer) { - vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf, - state); - vin->buf_hw[i].buffer = NULL; + freed[i] = vin->buf_hw[i].buffer; + vin->buf_hw[i].buffer = NULL; + + for (n = 0; n < i; n++) { + if (freed[i] == freed[n]) { + freed[i] = NULL; + break; + } } + + if (freed[i]) + vb2_buffer_done(&freed[i]->vb2_buf, state); } list_for_each_entry_safe(buf, node, &vin->buf_list, list) { diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index c60dcf444b02..d1b81881c3fa 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -118,6 +118,9 @@ static u32 rvin_format_bytesperline(struct rvin_dev *vin, break; } + if (V4L2_FIELD_IS_SEQUENTIAL(pix->field)) + align = 0x80; + return ALIGN(pix->width, align) * fmt->bpp; } @@ -148,6 +151,8 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix) case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: case V4L2_FIELD_ALTERNATE: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: break; default: pix->field = RVIN_DEFAULT_FIELD; diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 0aa904a4af5b..c19d077ce1cb 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -60,6 +60,23 @@ enum rvin_dma_state { STOPPING, }; +/** + * enum rvin_buffer_type + * + * Describes how a buffer is given to the hardware. To be able + * to capture SEQ_TB/BT it's needed to capture to the same vb2 + * buffer twice so the type of buffer needs to be kept. + * + * FULL - One capture fills the whole vb2 buffer + * HALF_TOP - One capture fills the top half of the vb2 buffer + * HALF_BOTTOM - One capture fills the bottom half of the vb2 buffer + */ +enum rvin_buffer_type { + FULL, + HALF_TOP, + HALF_BOTTOM, +}; + /** * struct rvin_video_format - Data format stored in memory * @fourcc: Pixelformat @@ -206,6 +223,8 @@ struct rvin_dev { spinlock_t qlock; struct { struct vb2_v4l2_buffer *buffer; + enum rvin_buffer_type type; + dma_addr_t phys; } buf_hw[HW_BUFFER_NUM]; struct list_head buf_list; unsigned int sequence; From bca243b1ce0e46be26f7c63b5591dfbb41f558e5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 3 Jan 2020 17:35:08 +0100 Subject: [PATCH 0585/2231] media: flexcop-usb: fix endpoint sanity check commit 1b976fc6d684 ("media: b2c2-flexcop-usb: add sanity checking") added an endpoint sanity check to address a NULL-pointer dereference on probe. Unfortunately the check was done on the current altsetting which was later changed. Fix this by moving the sanity check to after the altsetting is changed. Fixes: 1b976fc6d684 ("media: b2c2-flexcop-usb: add sanity checking") Cc: Oliver Neukum Cc: stable Signed-off-by: Johan Hovold Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/b2c2/flexcop-usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c index 039963a7765b..198ddfb8d2b1 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -511,6 +511,9 @@ static int flexcop_usb_init(struct flexcop_usb *fc_usb) return ret; } + if (fc_usb->uintf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + switch (fc_usb->udev->speed) { case USB_SPEED_LOW: err("cannot handle USB speed because it is too slow."); @@ -544,9 +547,6 @@ static int flexcop_usb_probe(struct usb_interface *intf, struct flexcop_device *fc = NULL; int ret; - if (intf->cur_altsetting->desc.bNumEndpoints < 1) - return -ENODEV; - if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { err("out of memory\n"); return -ENOMEM; From f52981019ad8d6718de79b425a574c6bddf81f7c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 3 Jan 2020 17:35:12 +0100 Subject: [PATCH 0586/2231] media: dib0700: fix rc endpoint lookup Make sure to use the current alternate setting when verifying the interface descriptors to avoid submitting an URB to an invalid endpoint. Failing to do so could cause the driver to misbehave or trigger a WARN() in usb_submit_urb() that kernels with panic_on_warn set would choke on. Fixes: c4018fa2e4c0 ("[media] dib0700: fix RC support on Hauppauge Nova-TD") Cc: stable # 3.16 Signed-off-by: Johan Hovold Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index e53c58ab6488..ef62dd6c5ae4 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -818,7 +818,7 @@ int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf) /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */ - if (intf->altsetting[0].desc.bNumEndpoints < rc_ep + 1) + if (intf->cur_altsetting->desc.bNumEndpoints < rc_ep + 1) return -ENODEV; purb = usb_alloc_urb(0, GFP_KERNEL); @@ -838,7 +838,7 @@ int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf) * Some devices like the Hauppauge NovaTD model 52009 use an interrupt * endpoint, while others use a bulk one. */ - e = &intf->altsetting[0].endpoint[rc_ep].desc; + e = &intf->cur_altsetting->endpoint[rc_ep].desc; if (usb_endpoint_dir_in(e)) { if (usb_endpoint_xfer_bulk(e)) { pipe = usb_rcvbulkpipe(d->udev, rc_ep); From ef45b77b62ef9b4842e25c5b850023e4211c0b1e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 7 Jan 2020 18:11:07 +0100 Subject: [PATCH 0587/2231] media: drxj: remove redundant assignments to variable rc The variable rc is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drx39xyj/drxj.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index ac7be872f460..5de016412c42 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -2182,7 +2182,7 @@ int drxj_dap_atomic_read_reg32(struct i2c_device_addr *dev_addr, u32 *data, u32 flags) { u8 buf[sizeof(*data)] = { 0 }; - int rc = -EIO; + int rc; u32 word = 0; if (!data) @@ -4229,7 +4229,7 @@ int drxj_dap_scu_atomic_write_reg16(struct i2c_device_addr *dev_addr, u16 data, u32 flags) { u8 buf[2]; - int rc = -EIO; + int rc; buf[0] = (u8) (data & 0xff); buf[1] = (u8) ((data >> 8) & 0xff); From 30defecb98400575349a7d32f0526e1dc42ea83e Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Fri, 10 Jan 2020 17:25:45 +0100 Subject: [PATCH 0588/2231] media: rc: add keymap for Videostrong KII Pro This is an NEC remote control device shipped with the Videostrong KII Pro tv box as well as other devices from videostrong. Signed-off-by: Mohammad Rasim Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + .../media/rc/keymaps/rc-videostrong-kii-pro.c | 83 +++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 85 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-videostrong-kii-pro.c diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 63261ef6380a..aaa1bf81d00d 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-videomate-m1f.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ + rc-videostrong-kii-pro.o \ rc-wetek-hub.o \ rc-wetek-play2.o \ rc-winfast.o \ diff --git a/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c b/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c new file mode 100644 index 000000000000..414d4d231e7e --- /dev/null +++ b/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2019 Mohammad Rasim + +#include +#include + +// +// Keytable for the Videostrong KII Pro STB remote control +// + +static struct rc_map_table kii_pro[] = { + { 0x59, KEY_POWER }, + { 0x19, KEY_MUTE }, + { 0x42, KEY_RED }, + { 0x40, KEY_GREEN }, + { 0x00, KEY_YELLOW }, + { 0x03, KEY_BLUE }, + { 0x4a, KEY_BACK }, + { 0x48, KEY_FORWARD }, + { 0x08, KEY_PREVIOUSSONG}, + { 0x0b, KEY_NEXTSONG}, + { 0x46, KEY_PLAYPAUSE }, + { 0x44, KEY_STOP }, + { 0x1f, KEY_FAVORITES}, //KEY_F5? + { 0x04, KEY_PVR }, + { 0x4d, KEY_EPG }, + { 0x02, KEY_INFO }, + { 0x09, KEY_SUBTITLE }, + { 0x01, KEY_AUDIO }, + { 0x0d, KEY_HOMEPAGE }, + { 0x11, KEY_TV }, // DTV ? + { 0x06, KEY_UP }, + { 0x5a, KEY_LEFT }, + { 0x1a, KEY_ENTER }, // KEY_OK ? + { 0x1b, KEY_RIGHT }, + { 0x16, KEY_DOWN }, + { 0x45, KEY_MENU }, + { 0x05, KEY_ESC }, + { 0x13, KEY_VOLUMEUP }, + { 0x17, KEY_VOLUMEDOWN }, + { 0x58, KEY_APPSELECT }, + { 0x12, KEY_VENDOR }, // mouse + { 0x55, KEY_PAGEUP }, // KEY_CHANNELUP ? + { 0x15, KEY_PAGEDOWN }, // KEY_CHANNELDOWN ? + { 0x52, KEY_1 }, + { 0x50, KEY_2 }, + { 0x10, KEY_3 }, + { 0x56, KEY_4 }, + { 0x54, KEY_5 }, + { 0x14, KEY_6 }, + { 0x4e, KEY_7 }, + { 0x4c, KEY_8 }, + { 0x0c, KEY_9 }, + { 0x18, KEY_WWW }, // KEY_F7 + { 0x0f, KEY_0 }, + { 0x51, KEY_BACKSPACE }, +}; + +static struct rc_map_list kii_pro_map = { + .map = { + .scan = kii_pro, + .size = ARRAY_SIZE(kii_pro), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_KII_PRO, + } +}; + +static int __init init_rc_map_kii_pro(void) +{ + return rc_map_register(&kii_pro_map); +} + +static void __exit exit_rc_map_kii_pro(void) +{ + rc_map_unregister(&kii_pro_map); +} + +module_init(init_rc_map_kii_pro) +module_exit(exit_rc_map_kii_pro) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mohammad Rasim "); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index f99575a0d29c..d22810dcd85c 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -274,6 +274,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_VIDEOMATE_K100 "rc-videomate-k100" #define RC_MAP_VIDEOMATE_S350 "rc-videomate-s350" #define RC_MAP_VIDEOMATE_TV_PVR "rc-videomate-tv-pvr" +#define RC_MAP_KII_PRO "rc-videostrong-kii-pro" #define RC_MAP_WETEK_HUB "rc-wetek-hub" #define RC_MAP_WETEK_PLAY2 "rc-wetek-play2" #define RC_MAP_WINFAST "rc-winfast" From 6b212c0cd5c95fccb69f961284b0fa71ee2260f1 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Thu, 19 Dec 2019 08:48:24 +0100 Subject: [PATCH 0589/2231] media: exynos4-is: add missed clk_disable_unprepare in remove This driver forgets to disable and unprepare clock when remove. Add a call to clk_disable_unprepare() to fix it. Signed-off-by: Chuhong Yuan Reviewed-by: Krzysztof Kozlowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos4-is/fimc-lite.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index e87c6a09205b..17de14fbba31 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1614,6 +1614,9 @@ static int fimc_lite_remove(struct platform_device *pdev) struct fimc_lite *fimc = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + if (!pm_runtime_enabled(dev)) + clk_disable_unprepare(fimc->clock); + pm_runtime_disable(dev); pm_runtime_set_suspended(dev); fimc_lite_unregister_capture_subdev(fimc); From cc62c74749a3cacfac126a6f6dfc2f40bd58a79e Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Mon, 13 Jan 2020 06:59:51 +0100 Subject: [PATCH 0590/2231] media: allegro: add missed checks in allegro_open() allegro_open() misses checks for v4l2_m2m_ctx_init() and results of v4l2_ctrl_new* calls. Add checks and error handlers to fix the problems. Signed-off-by: Chuhong Yuan Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/allegro-dvt/allegro-core.c | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 3be41698df4c..0c7428484085 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -2270,15 +2270,12 @@ static int allegro_open(struct file *file) struct allegro_channel *channel = NULL; struct v4l2_ctrl_handler *handler; u64 mask; + int ret; channel = kzalloc(sizeof(*channel), GFP_KERNEL); if (!channel) return -ENOMEM; - v4l2_fh_init(&channel->fh, vdev); - file->private_data = &channel->fh; - v4l2_fh_add(&channel->fh); - init_completion(&channel->completion); channel->dev = dev; @@ -2328,6 +2325,11 @@ static int allegro_open(struct file *file) V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1); + if (handler->error != 0) { + ret = handler->error; + goto error; + } + channel->fh.ctrl_handler = handler; channel->mcu_channel_id = -1; @@ -2341,7 +2343,21 @@ static int allegro_open(struct file *file) channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, allegro_queue_init); + if (IS_ERR(channel->fh.m2m_ctx)) { + ret = PTR_ERR(channel->fh.m2m_ctx); + goto error; + } + + v4l2_fh_init(&channel->fh, vdev); + file->private_data = &channel->fh; + v4l2_fh_add(&channel->fh); + return 0; + +error: + v4l2_ctrl_handler_free(handler); + kfree(channel); + return ret; } static int allegro_release(struct file *file) From 238e4a5baa361256ae1641ad9455bb2bb359273f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:09 +0100 Subject: [PATCH 0591/2231] media: rename VFL_TYPE_GRABBER to _VIDEO We currently have the following devnode types: enum vfl_devnode_type { VFL_TYPE_GRABBER = 0, VFL_TYPE_VBI, VFL_TYPE_RADIO, VFL_TYPE_SUBDEV, VFL_TYPE_SDR, VFL_TYPE_TOUCH, VFL_TYPE_MAX /* Shall be the last one */ }; They all make sense, except for the first: GRABBER really refers to /dev/videoX devices, which can be capture, output or m2m, so 'grabber' doesn't even refer to their function anymore. Let's call a spade a spade and rename this to VFL_TYPE_VIDEO. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/kapi/v4l2-dev.rst | 4 ++-- .../translations/zh_CN/video4linux/v4l2-framework.txt | 4 ++-- drivers/media/v4l2-core/v4l2-dev.c | 10 +++++----- drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++-- include/media/v4l2-dev.h | 6 ++++-- samples/v4l/v4l2-pci-skeleton.c | 2 +- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Documentation/media/kapi/v4l2-dev.rst b/Documentation/media/kapi/v4l2-dev.rst index 4c5a15c53dbf..63c064837c00 100644 --- a/Documentation/media/kapi/v4l2-dev.rst +++ b/Documentation/media/kapi/v4l2-dev.rst @@ -185,7 +185,7 @@ This will create the character device for you. .. code-block:: c - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (err) { video_device_release(vdev); /* or kfree(my_vdev); */ return err; @@ -201,7 +201,7 @@ types exist: ========================== ==================== ============================== :c:type:`vfl_devnode_type` Device name Usage ========================== ==================== ============================== -``VFL_TYPE_GRABBER`` ``/dev/videoX`` for video input/output devices +``VFL_TYPE_VIDEO`` ``/dev/videoX`` for video input/output devices ``VFL_TYPE_VBI`` ``/dev/vbiX`` for vertical blank data (i.e. closed captions, teletext) ``VFL_TYPE_RADIO`` ``/dev/radioX`` for radio tuners diff --git a/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt b/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt index 66c7c568bd86..9c39ee58ea50 100644 --- a/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt +++ b/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt @@ -649,7 +649,7 @@ video_device注册 接下来你需要注册视频设备:这会为你创建一个字符设备。 - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (err) { video_device_release(vdev); /* or kfree(my_vdev); */ return err; @@ -660,7 +660,7 @@ video_device注册 注册哪种设备是根据类型(type)参数。存在以下类型: -VFL_TYPE_GRABBER: 用于视频输入/输出设备的 videoX +VFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX VFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视) VFL_TYPE_RADIO: 用于广播调谐器的 radioX diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index da42d172714a..97b6a3af1361 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -542,13 +542,13 @@ static void determine_valid_ioctls(struct video_device *vdev) V4L2_CAP_META_OUTPUT; DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; - bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER && + bool is_vid = vdev->vfl_type == VFL_TYPE_VIDEO && (vdev->device_caps & vid_caps); bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI; bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO; bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR; bool is_tch = vdev->vfl_type == VFL_TYPE_TOUCH; - bool is_meta = vdev->vfl_type == VFL_TYPE_GRABBER && + bool is_meta = vdev->vfl_type == VFL_TYPE_VIDEO && (vdev->device_caps & meta_caps); bool is_rx = vdev->vfl_dir != VFL_DIR_TX; bool is_tx = vdev->vfl_dir != VFL_DIR_RX; @@ -783,7 +783,7 @@ static int video_register_media_controller(struct video_device *vdev) vdev->entity.function = MEDIA_ENT_F_UNKNOWN; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: intf_type = MEDIA_INTF_T_V4L_VIDEO; vdev->entity.function = MEDIA_ENT_F_IO_V4L; break; @@ -891,7 +891,7 @@ int __video_register_device(struct video_device *vdev, /* Part 1: check device type */ switch (type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: name_base = "video"; break; case VFL_TYPE_VBI: @@ -935,7 +935,7 @@ int __video_register_device(struct video_device *vdev, * of 128-191 and just pick the first free minor there * (new style). */ switch (type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: minor_offset = 0; minor_cnt = 64; break; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index aaf83e254272..fbcc7a20eedf 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -941,12 +941,12 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) V4L2_CAP_META_OUTPUT; struct video_device *vfd = video_devdata(file); const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; - bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER && + bool is_vid = vfd->vfl_type == VFL_TYPE_VIDEO && (vfd->device_caps & vid_caps); bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; - bool is_meta = vfd->vfl_type == VFL_TYPE_GRABBER && + bool is_meta = vfd->vfl_type == VFL_TYPE_VIDEO && (vfd->device_caps & meta_caps); bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 48531e57cc5a..5e7c0f8acd05 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -24,7 +24,8 @@ /** * enum vfl_devnode_type - type of V4L2 device node * - * @VFL_TYPE_GRABBER: for video input/output devices + * @VFL_TYPE_VIDEO: for video input/output devices + * @VFL_TYPE_GRABBER: deprecated, same as VFL_TYPE_VIDEO * @VFL_TYPE_VBI: for vertical blank data (i.e. closed captions, teletext) * @VFL_TYPE_RADIO: for radio tuners * @VFL_TYPE_SUBDEV: for V4L2 subdevices @@ -33,7 +34,8 @@ * @VFL_TYPE_MAX: number of VFL types, must always be last in the enum */ enum vfl_devnode_type { - VFL_TYPE_GRABBER = 0, + VFL_TYPE_VIDEO, + VFL_TYPE_GRABBER = VFL_TYPE_VIDEO, VFL_TYPE_VBI, VFL_TYPE_RADIO, VFL_TYPE_SUBDEV, diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c index f6a551bd57ef..3fa6582b4a68 100644 --- a/samples/v4l/v4l2-pci-skeleton.c +++ b/samples/v4l/v4l2-pci-skeleton.c @@ -879,7 +879,7 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vdev->tvnorms = SKEL_TVNORMS; video_set_drvdata(vdev, skel); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto free_hdl; From 0ceba5502258c28362bffe1ceb259128ef77ad86 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:10 +0100 Subject: [PATCH 0592/2231] media: gadget: uvc: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/usb/gadget/function/f_uvc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index fb0a892687c0..0b9712616455 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -428,7 +428,7 @@ uvc_register_video(struct uvc_device *uvc) video_set_drvdata(&uvc->vdev, uvc); - ret = video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&uvc->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) return ret; From fd6b556faf456312e76ee89aa67e2957b76c3565 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:11 +0100 Subject: [PATCH 0593/2231] media: bcm2835-camera: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Cc: Laurent Pinchart Cc: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 1ef31a984741..597acef35d0b 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1776,7 +1776,7 @@ static int bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, video_set_drvdata(vfd, dev); ret = video_register_device(vfd, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, video_nr[dev->camera_num]); if (ret < 0) return ret; From 0e17c50fcf6a9e11d616ab30e517b758751cb853 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:12 +0100 Subject: [PATCH 0594/2231] media: staging/media: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 2 +- drivers/staging/media/hantro/hantro_drv.c | 2 +- drivers/staging/media/imx/imx-media-capture.c | 2 +- drivers/staging/media/imx/imx-media-csc-scaler.c | 2 +- drivers/staging/media/ipu3/ipu3-v4l2.c | 2 +- drivers/staging/media/meson/vdec/vdec.c | 2 +- drivers/staging/media/omap4iss/iss_video.c | 4 ++-- drivers/staging/media/rkisp1/rkisp1-capture.c | 2 +- drivers/staging/media/rkisp1/rkisp1-params.c | 2 +- drivers/staging/media/rkisp1/rkisp1-stats.c | 2 +- drivers/staging/media/soc_camera/soc_camera.c | 2 +- drivers/staging/media/sunxi/cedrus/cedrus.c | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 0c7428484085..ef53730fff6b 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -2717,7 +2717,7 @@ static int allegro_register_device(struct allegro_dev *dev) video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; video_set_drvdata(video_dev, dev); - return video_register_device(video_dev, VFL_TYPE_GRABBER, 0); + return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); } static void allegro_device_run(void *priv) diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index ca8b133e2e46..0b1200fc0e1a 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -674,7 +674,7 @@ static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid) video_set_drvdata(vfd, vpu); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); return ret; diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 7712e7be8625..fca8f3847717 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -742,7 +742,7 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) vfd->v4l2_dev = v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(sd, "Failed to register video device\n"); return ret; diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c index 2b635ebf62d6..2cc77f6e84b6 100644 --- a/drivers/staging/media/imx/imx-media-csc-scaler.c +++ b/drivers/staging/media/imx/imx-media-csc-scaler.c @@ -849,7 +849,7 @@ int imx_media_csc_scaler_device_register(struct imx_media_video_dev *vdev) vfd->v4l2_dev = &priv->md->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(vfd->v4l2_dev, "Failed to register video device\n"); return ret; diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 569e27b824c8..09c8ede1457c 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -1245,7 +1245,7 @@ static int imgu_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, vdev->queue = &node->vbq; vdev->vfl_dir = node->output ? VFL_DIR_TX : VFL_DIR_RX; video_set_drvdata(vdev, imgu); - r = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + r = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (r) { dev_err(dev, "failed to register video device (%d)", r); media_entity_cleanup(&vdev->entity); diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 5c5dabed2f09..7a9cc136afd9 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -1076,7 +1076,7 @@ static int vdec_probe(struct platform_device *pdev) video_set_drvdata(vdev, core); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(dev, "Failed registering video device\n"); goto err_vdev_release; diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 673aa3a5f2bd..bf7e999998c3 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -1242,7 +1242,7 @@ int omap4iss_video_init(struct iss_video *video, const char *name) video->video.fops = &iss_video_fops; snprintf(video->video.name, sizeof(video->video.name), "OMAP4 ISS %s %s", name, direction); - video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.vfl_type = VFL_TYPE_VIDEO; video->video.release = video_device_release_empty; video->video.ioctl_ops = &iss_video_ioctl_ops; video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED; @@ -1270,7 +1270,7 @@ int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev) video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT; video->video.device_caps |= V4L2_CAP_STREAMING; - ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); if (ret < 0) dev_err(video->iss->dev, "could not register video device (%d)\n", ret); diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 524e0dd38c1b..7cc2ace193d1 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -1362,7 +1362,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) vdev->queue = q; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(cap->rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/rkisp1/rkisp1-params.c b/drivers/staging/media/rkisp1/rkisp1-params.c index 781f0ca85af1..44d542caf32b 100644 --- a/drivers/staging/media/rkisp1/rkisp1-params.c +++ b/drivers/staging/media/rkisp1/rkisp1-params.c @@ -1605,7 +1605,7 @@ int rkisp1_params_register(struct rkisp1_params *params, ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret) goto err_release_queue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&vdev->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index d98ea15837de..c0ccc86b1275 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -487,7 +487,7 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, if (ret) goto err_release_queue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&vdev->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c index 7b9448e3c9ba..39f513f69b89 100644 --- a/drivers/staging/media/soc_camera/soc_camera.c +++ b/drivers/staging/media/soc_camera/soc_camera.c @@ -2068,7 +2068,7 @@ static int soc_camera_video_start(struct soc_camera_device *icd) v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); } - ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(icd->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(icd->pdev, "video_register_device failed: %d\n", ret); return ret; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index c6ddd46eff82..05a85517ff60 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -414,7 +414,7 @@ static int cedrus_probe(struct platform_device *pdev) dev->mdev.ops = &cedrus_m2m_media_ops; dev->v4l2_dev.mdev = &dev->mdev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_m2m; From e653614ee1832ce6bacc4f177e0495b5a501b798 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:13 +0100 Subject: [PATCH 0595/2231] media: staging/most: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Cc: Christian Gromm Reviewed-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/most/video/video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index d32ae49d617b..4cffc8fd62bd 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -74,7 +74,7 @@ static int comp_vdev_open(struct file *filp) struct comp_fh *fh; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: break; default: return -EINVAL; @@ -424,7 +424,7 @@ static int comp_register_videodev(struct most_video_dev *mdev) /* Register the v4l2 device */ video_set_drvdata(mdev->vdev, mdev); - ret = video_register_device(mdev->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(mdev->vdev, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n", ret); From 7fbbbc780eb30f0998234695ca7da15648af049b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:14 +0100 Subject: [PATCH 0596/2231] media: media/usb: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-video.c | 4 ++-- drivers/media/usb/cpia2/cpia2_v4l.c | 2 +- drivers/media/usb/cx231xx/cx231xx-417.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- drivers/media/usb/dvb-usb/cxusb-analog.c | 12 ++++++------ drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/go7007/go7007-v4l2.c | 2 +- drivers/media/usb/gspca/gspca.c | 2 +- drivers/media/usb/hdpvr/hdpvr-video.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 4 ++-- drivers/media/usb/pwc/pwc-if.c | 2 +- drivers/media/usb/s2255/s2255drv.c | 4 ++-- drivers/media/usb/stk1160/stk1160-v4l.c | 2 +- drivers/media/usb/stkwebcam/stk-webcam.c | 2 +- drivers/media/usb/tm6000/tm6000-video.c | 4 ++-- drivers/media/usb/usbtv/usbtv-video.c | 2 +- drivers/media/usb/usbvision/usbvision-video.c | 2 +- drivers/media/usb/uvc/uvc_driver.c | 2 +- drivers/media/usb/zr364xx/zr364xx.c | 2 +- 19 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index d1895334cbbf..51b8d14fb4dc 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1042,7 +1042,7 @@ static int au0828_v4l2_close(struct file *filp) dev->streaming_users, dev->users); mutex_lock(&dev->lock); - if (vdev->vfl_type == VFL_TYPE_GRABBER && dev->vid_timeout_running) { + if (vdev->vfl_type == VFL_TYPE_VIDEO && dev->vid_timeout_running) { /* Cancel timeout thread in case they didn't call streamoff */ dev->vid_timeout_running = 0; del_timer_sync(&dev->vid_timeout); @@ -2007,7 +2007,7 @@ int au0828_analog_register(struct au0828_dev *dev, /* Register the v4l2 device */ video_set_drvdata(&dev->vdev, dev); - retval = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + retval = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); if (retval != 0) { dprintk(1, "unable to register video device (error = %d).\n", retval); diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index 9d3d05125d7b..e488e7870f42 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -1134,7 +1134,7 @@ int cpia2_register_camera(struct camera_data *cam) reset_camera_struct_v4l(cam); /* register v4l device */ - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + if (video_register_device(&cam->vdev, VFL_TYPE_VIDEO, video_nr) < 0) { ERR("video_register_device failed\n"); return -ENODEV; } diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 1aec4459f50a..b0cd51134654 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1790,7 +1790,7 @@ int cx231xx_417_register(struct cx231xx *dev) dev->v4l_device.queue = q; err = video_register_device(&dev->v4l_device, - VFL_TYPE_GRABBER, -1); + VFL_TYPE_VIDEO, -1); if (err < 0) { dprintk(3, "%s: can't register mpeg device\n", dev->name); v4l2_ctrl_handler_free(&dev->mpeg_ctrl_handler.hdl); diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 69abafaebbf3..8bff7d8a0310 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1785,7 +1785,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev->vdev.device_caps |= V4L2_CAP_TUNER; /* register v4l2 video video_device */ - ret = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, + ret = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, video_nr[dev->devno]); if (ret) { dev_err(dev->dev, diff --git a/drivers/media/usb/dvb-usb/cxusb-analog.c b/drivers/media/usb/dvb-usb/cxusb-analog.c index 0699f718d052..001cae648797 100644 --- a/drivers/media/usb/dvb-usb/cxusb-analog.c +++ b/drivers/media/usb/dvb-usb/cxusb-analog.c @@ -1223,7 +1223,7 @@ static int cxusb_medion_g_tuner(struct file *file, void *fh, if (tuner->index != 0) return -EINVAL; - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) tuner->type = V4L2_TUNER_ANALOG_TV; else tuner->type = V4L2_TUNER_RADIO; @@ -1259,7 +1259,7 @@ static int cxusb_medion_g_tuner(struct file *file, void *fh, if (ret != 0) return ret; - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) strscpy(tuner->name, "TV tuner", sizeof(tuner->name)); else strscpy(tuner->name, "Radio tuner", sizeof(tuner->name)); @@ -1292,7 +1292,7 @@ static int cxusb_medion_s_tuner(struct file *file, void *fh, * make sure that cx25840 is in a correct TV / radio mode, * since calls above may have changed it for tuner / IF demod */ - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); else v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); @@ -1335,7 +1335,7 @@ static int cxusb_medion_s_frequency(struct file *file, void *fh, * make sure that cx25840 is in a correct TV / radio mode, * since calls above may have changed it for tuner / IF demod */ - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); else v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); @@ -1564,7 +1564,7 @@ static int cxusb_videoradio_release(struct file *f) cxusb_vprintk(dvbdev, OPS, "got release\n"); - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) ret = vb2_fop_release(f); else ret = v4l2_fh_release(f); @@ -1663,7 +1663,7 @@ static int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) cxdev->videodev->lock = &cxdev->dev_lock; video_set_drvdata(cxdev->videodev, dvbdev); - ret = video_register_device(cxdev->videodev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(cxdev->videodev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&dvbdev->udev->dev, "video device register failed, ret = %d\n", ret); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index b0f7390e4b4f..6b84c3413e83 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2141,7 +2141,7 @@ static int em28xx_v4l2_open(struct file *filp) int ret; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; break; case VFL_TYPE_VBI: @@ -2789,7 +2789,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) } /* register v4l2 video video_device */ - ret = video_register_device(&v4l2->vdev, VFL_TYPE_GRABBER, + ret = video_register_device(&v4l2->vdev, VFL_TYPE_VIDEO, video_nr[dev->devno]); if (ret) { dev_err(&dev->intf->dev, diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c index 0b3d185f3cb0..b2edc4deaca3 100644 --- a/drivers/media/usb/go7007/go7007-v4l2.c +++ b/drivers/media/usb/go7007/go7007-v4l2.c @@ -1138,7 +1138,7 @@ int go7007_v4l2_init(struct go7007 *go) go7007_s_input(go); if (go->board_info->sensor_flags & GO7007_SENSOR_TV) go7007_s_std(go); - rv = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + rv = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (rv < 0) return rv; dev_info(go->dev, "registered device %s [v4l2]\n", diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index c1b307bbe540..0566e00d6fea 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1555,7 +1555,7 @@ int gspca_dev_probe2(struct usb_interface *intf, /* init video stuff */ ret = video_register_device(&gspca_dev->vdev, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, -1); if (ret < 0) { pr_err("video_register_device err %d\n", ret); diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index bad71d863d39..563128d11731 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -1238,7 +1238,7 @@ int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, dev->video_dev.v4l2_dev = &dev->v4l2_dev; video_set_drvdata(&dev->video_dev, dev); - res = video_register_device(&dev->video_dev, VFL_TYPE_GRABBER, devnum); + res = video_register_device(&dev->video_dev, VFL_TYPE_VIDEO, devnum); if (res < 0) { v4l2_err(&dev->v4l2_dev, "video_device registration failed\n"); goto error; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index eaa08c7999d4..9657c1883311 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -1196,7 +1196,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, hdw = vp->channel.mc_head->hdw; dip->v4l_type = v4l_type; switch (v4l_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: dip->stream = &vp->channel.mc_head->video_stream; dip->config = pvr2_config_mpeg; dip->minor_type = pvr2_v4l_type_video; @@ -1276,7 +1276,7 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) /* register streams */ vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); if (!vp->dev_video) goto fail; - pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); + pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_VIDEO); if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) & (1 << PVR2_CVAL_INPUT_RADIO)) { vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 9b76cf133d52..d57b8b786506 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1116,7 +1116,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); + rc = video_register_device(&pdev->vdev, VFL_TYPE_VIDEO, -1); if (rc < 0) { PWC_ERROR("Failed to register as video device (%d).\n", rc); goto err_unregister_v4l2_dev; diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 329ec8089592..4af55e2478be 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1649,11 +1649,11 @@ static int s2255_probe_v4l(struct s2255_dev *dev) video_set_drvdata(&vc->vdev, vc); if (video_nr == -1) ret = video_register_device(&vc->vdev, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, video_nr); else ret = video_register_device(&vc->vdev, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, cur_nr + i); if (ret) { diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index bcd14c66e8df..6a4eb616d516 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -830,7 +830,7 @@ int stk1160_video_register(struct stk1160 *dev) dev->norm); video_set_drvdata(&dev->vdev, dev); - rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + rc = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); if (rc < 0) { stk1160_err("video_register_device failed (%d)\n", rc); return rc; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index b22501f76b78..a45d464427c4 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1254,7 +1254,7 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; video_set_drvdata(&dev->vdev, dev); - err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); if (err) pr_err("v4l registration failed\n"); else diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index c07a81a6cbe2..bfba06ea60e9 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1300,7 +1300,7 @@ static int __tm6000_open(struct file *file) video_device_node_name(vdev)); switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: type = V4L2_BUF_TYPE_VIDEO_CAPTURE; break; case VFL_TYPE_VBI: @@ -1639,7 +1639,7 @@ int tm6000_v4l2_register(struct tm6000_core *dev) INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); - ret = video_register_device(&dev->vfd, VFL_TYPE_GRABBER, video_nr); + ret = video_register_device(&dev->vfd, VFL_TYPE_VIDEO, video_nr); if (ret < 0) { printk(KERN_INFO "%s: can't register video device\n", diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index b249f037900c..c89efcd46163 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -941,7 +941,7 @@ int usbtv_video_init(struct usbtv *usbtv) usbtv->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; video_set_drvdata(&usbtv->vdev, usbtv); - ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&usbtv->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_warn(usbtv->dev, "Could not register video device\n"); goto vdev_fail; diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 5ca2c2f35fe2..3ea25fdcf767 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1271,7 +1271,7 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) if (usbvision->have_tuner) usbvision->vdev.device_caps |= V4L2_CAP_TUNER; - if (video_register_device(&usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0) + if (video_register_device(&usbvision->vdev, VFL_TYPE_VIDEO, video_nr) < 0) goto err_exit; printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", usbvision->nr, video_device_node_name(&usbvision->vdev)); diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 99883550375e..431d86e1c94b 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2014,7 +2014,7 @@ int uvc_register_video_device(struct uvc_device *dev, */ video_set_drvdata(vdev, stream); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { uvc_printk(KERN_ERR, "Failed to register %s device (%d).\n", v4l2_type_names[type], ret); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 57dbcc8083bf..8c670934d920 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -1516,7 +1516,7 @@ static int zr364xx_probe(struct usb_interface *intf, V4L2_FIELD_NONE, sizeof(struct zr364xx_buffer), cam, &cam->lock); - err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); if (err) { dev_err(&udev->dev, "video_register_device failed\n"); goto fail; From a911268349953febdb860651a872973c409a77db Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:15 +0100 Subject: [PATCH 0597/2231] media: media/common/saa7146: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index d7e83b55ddca..d6531874faa6 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -294,7 +294,7 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma) int res; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: { + case VFL_TYPE_VIDEO: { DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", file, vma); q = &fh->video_q; @@ -376,7 +376,7 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof int ret; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: /* DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count); @@ -407,7 +407,7 @@ static ssize_t fops_write(struct file *file, const char __user *data, size_t cou int ret; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: return -EINVAL; case VFL_TYPE_VBI: if (fh->dev->ext_vv_data->vbi_fops.write) { @@ -595,7 +595,7 @@ int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); vfd->fops = &video_fops; - if (type == VFL_TYPE_GRABBER) + if (type == VFL_TYPE_VIDEO) vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; else vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; @@ -609,7 +609,7 @@ int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; vfd->device_caps |= dev->ext_vv_data->capabilities; - if (type == VFL_TYPE_GRABBER) + if (type == VFL_TYPE_VIDEO) vfd->device_caps &= ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); else From 6347953e0df1a38da11ab9088d8f60f73589bad4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:16 +0100 Subject: [PATCH 0598/2231] media: media/i2c/video-i2c: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Cc: Matt Ranostay Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/video-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 078141712c88..3c770ecd4bf7 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -858,7 +858,7 @@ static int video_i2c_probe(struct i2c_client *client, } } - ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&data->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) goto error_pm_disable; From 3e30a927af3ca86f6556faf437da44ca8bcf54a4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:17 +0100 Subject: [PATCH 0599/2231] media: media/pci: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-driver.c | 4 ++-- drivers/media/pci/cobalt/cobalt-v4l2.c | 2 +- drivers/media/pci/cx18/cx18-streams.c | 12 ++++++------ drivers/media/pci/cx23885/cx23885-417.c | 2 +- drivers/media/pci/cx23885/cx23885-video.c | 2 +- drivers/media/pci/cx25821/cx25821-video.c | 2 +- drivers/media/pci/cx88/cx88-blackbird.c | 2 +- drivers/media/pci/cx88/cx88-video.c | 2 +- drivers/media/pci/dt3155/dt3155.c | 2 +- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 2 +- drivers/media/pci/ivtv/ivtv-streams.c | 12 ++++++------ drivers/media/pci/meye/meye.c | 2 +- drivers/media/pci/saa7134/saa7134-core.c | 2 +- drivers/media/pci/saa7134/saa7134-empress.c | 2 +- drivers/media/pci/saa7146/hexium_gemini.c | 2 +- drivers/media/pci/saa7146/hexium_orion.c | 2 +- drivers/media/pci/saa7146/mxb.c | 2 +- drivers/media/pci/saa7164/saa7164-encoder.c | 2 +- drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c | 2 +- drivers/media/pci/solo6x10/solo6x10-v4l2.c | 2 +- drivers/media/pci/sta2x11/sta2x11_vip.c | 2 +- drivers/media/pci/ttpci/av7110_v4l.c | 2 +- drivers/media/pci/ttpci/budget-av.c | 2 +- drivers/media/pci/tw5864/tw5864-video.c | 2 +- drivers/media/pci/tw68/tw68-video.c | 2 +- drivers/media/pci/tw686x/tw686x-video.c | 2 +- 26 files changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index a359da7773a9..9144f795fb93 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2964,7 +2964,7 @@ static int bttv_open(struct file *file) dprintk("open dev=%s\n", video_device_node_name(vdev)); - if (vdev->vfl_type == VFL_TYPE_GRABBER) { + if (vdev->vfl_type == VFL_TYPE_VIDEO) { type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } else if (vdev->vfl_type == VFL_TYPE_VBI) { type = V4L2_BUF_TYPE_VBI_CAPTURE; @@ -3905,7 +3905,7 @@ static int bttv_register_video(struct bttv *btv) if (no_overlay <= 0) btv->video_dev.device_caps |= V4L2_CAP_VIDEO_OVERLAY; - if (video_register_device(&btv->video_dev, VFL_TYPE_GRABBER, + if (video_register_device(&btv->video_dev, VFL_TYPE_VIDEO, video_nr[btv->c.nr]) < 0) goto err; pr_info("%d: registered device %s\n", diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c index c5207501d5e0..0ff37496c9ab 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.c +++ b/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -1272,7 +1272,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node) video_set_drvdata(vdev, s); ret = vb2_queue_init(q); if (!s->is_audio && ret == 0) - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); else if (!s->is_dummy) ret = cobalt_alsa_init(s); diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c index b79718519b9b..3178df3c4922 100644 --- a/drivers/media/pci/cx18/cx18-streams.c +++ b/drivers/media/pci/cx18/cx18-streams.c @@ -48,19 +48,19 @@ static struct { } cx18_stream_info[] = { { /* CX18_ENC_STREAM_TYPE_MPG */ "encoder MPEG", - VFL_TYPE_GRABBER, 0, + VFL_TYPE_VIDEO, 0, PCI_DMA_FROMDEVICE, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_AUDIO | V4L2_CAP_TUNER }, { /* CX18_ENC_STREAM_TYPE_TS */ "TS", - VFL_TYPE_GRABBER, -1, + VFL_TYPE_VIDEO, -1, PCI_DMA_FROMDEVICE, }, { /* CX18_ENC_STREAM_TYPE_YUV */ "encoder YUV", - VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET, + VFL_TYPE_VIDEO, CX18_V4L2_ENC_YUV_OFFSET, PCI_DMA_FROMDEVICE, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_TUNER @@ -74,13 +74,13 @@ static struct { }, { /* CX18_ENC_STREAM_TYPE_PCM */ "encoder PCM audio", - VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET, + VFL_TYPE_VIDEO, CX18_V4L2_ENC_PCM_OFFSET, PCI_DMA_FROMDEVICE, V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, }, { /* CX18_ENC_STREAM_TYPE_IDX */ "encoder IDX", - VFL_TYPE_GRABBER, -1, + VFL_TYPE_VIDEO, -1, PCI_DMA_FROMDEVICE, }, { /* CX18_ENC_STREAM_TYPE_RAD */ @@ -434,7 +434,7 @@ static int cx18_reg_dev(struct cx18 *cx, int type) name = video_device_node_name(&s->video_dev); switch (vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n", name, s->name, cx->stream_buffers[type], cx->stream_buf_size[type] / 1024, diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index 2327fe612610..434677bd4ad1 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -1545,7 +1545,7 @@ int cx23885_417_register(struct cx23885_dev *dev) if (dev->tuner_type != TUNER_ABSENT) dev->v4l_device->device_caps |= V4L2_CAP_TUNER; err = video_register_device(dev->v4l_device, - VFL_TYPE_GRABBER, -1); + VFL_TYPE_VIDEO, -1); if (err < 0) { pr_info("%s: can't register mpeg device\n", dev->name); return err; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 7fc408ee4934..000c108b94fd 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1304,7 +1304,7 @@ int cx23885_video_register(struct cx23885_dev *dev) V4L2_CAP_AUDIO | V4L2_CAP_VIDEO_CAPTURE; if (dev->tuner_type != TUNER_ABSENT) dev->video_dev->device_caps |= V4L2_CAP_TUNER; - err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, + err = video_register_device(dev->video_dev, VFL_TYPE_VIDEO, video_nr[dev->nr]); if (err < 0) { pr_info("%s: can't register video device\n", diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index a10261da0db6..1b80c990cb94 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -757,7 +757,7 @@ int cx25821_video_register(struct cx25821_dev *dev) snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i); video_set_drvdata(vdev, chan); - err = video_register_device(vdev, VFL_TYPE_GRABBER, + err = video_register_device(vdev, VFL_TYPE_VIDEO, video_nr[dev->nr]); if (err < 0) diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index d3da7f4297af..fa4ca002ed19 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -1138,7 +1138,7 @@ static int blackbird_register_video(struct cx8802_dev *dev) V4L2_CAP_VIDEO_CAPTURE; if (dev->core->board.tuner_type != UNSET) dev->mpeg_dev.device_caps |= V4L2_CAP_TUNER; - err = video_register_device(&dev->mpeg_dev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&dev->mpeg_dev, VFL_TYPE_VIDEO, -1); if (err < 0) { pr_info("can't register mpeg device\n"); return err; diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index b8abcd550604..6aabc45aa93c 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1451,7 +1451,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, V4L2_CAP_VIDEO_CAPTURE; if (core->board.tuner_type != UNSET) dev->video_dev.device_caps |= V4L2_CAP_TUNER; - err = video_register_device(&dev->video_dev, VFL_TYPE_GRABBER, + err = video_register_device(&dev->video_dev, VFL_TYPE_VIDEO, video_nr[core->nr]); if (err < 0) { pr_err("can't register video device\n"); diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index 7480f0d3ad0f..82581aa5a2a3 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -550,7 +550,7 @@ static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) IRQF_SHARED, DT3155_NAME, pd); if (err) goto err_iounmap; - err = video_register_device(&pd->vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&pd->vdev, VFL_TYPE_VIDEO, -1); if (err) goto err_free_irq; dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev.minor); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 1adfdc7ab0db..92f5eadf2c99 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1647,7 +1647,7 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) vdev->queue = &q->vbq; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; video_set_drvdata(vdev, cio2); - r = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + r = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (r) { dev_err(&cio2->pci_dev->dev, "failed to register video device (%d)\n", r); diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c index f7de9118f609..f04ee84bab5f 100644 --- a/drivers/media/pci/ivtv/ivtv-streams.c +++ b/drivers/media/pci/ivtv/ivtv-streams.c @@ -99,7 +99,7 @@ static struct { } ivtv_stream_info[] = { { /* IVTV_ENC_STREAM_TYPE_MPG */ "encoder MPG", - VFL_TYPE_GRABBER, 0, + VFL_TYPE_VIDEO, 0, PCI_DMA_FROMDEVICE, 0, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, @@ -107,7 +107,7 @@ static struct { }, { /* IVTV_ENC_STREAM_TYPE_YUV */ "encoder YUV", - VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, + VFL_TYPE_VIDEO, IVTV_V4L2_ENC_YUV_OFFSET, PCI_DMA_FROMDEVICE, 0, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, @@ -123,7 +123,7 @@ static struct { }, { /* IVTV_ENC_STREAM_TYPE_PCM */ "encoder PCM", - VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, + VFL_TYPE_VIDEO, IVTV_V4L2_ENC_PCM_OFFSET, PCI_DMA_FROMDEVICE, 0, V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops @@ -137,7 +137,7 @@ static struct { }, { /* IVTV_DEC_STREAM_TYPE_MPG */ "decoder MPG", - VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, + VFL_TYPE_VIDEO, IVTV_V4L2_DEC_MPG_OFFSET, PCI_DMA_TODEVICE, 0, V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops @@ -158,7 +158,7 @@ static struct { }, { /* IVTV_DEC_STREAM_TYPE_YUV */ "decoder YUV", - VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, + VFL_TYPE_VIDEO, IVTV_V4L2_DEC_YUV_OFFSET, PCI_DMA_TODEVICE, 0, V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops @@ -318,7 +318,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) name = video_device_node_name(&s->vdev); switch (vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: IVTV_INFO("Registered device %s for %s (%d kB)\n", name, s->name, itv->options.kilobytes[type]); break; diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 3a4c29bc0ba5..73e064e6f56d 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1711,7 +1711,7 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) v4l2_ctrl_handler_setup(&meye.hdl); meye.vdev.ctrl_handler = &meye.hdl; - if (video_register_device(&meye.vdev, VFL_TYPE_GRABBER, + if (video_register_device(&meye.vdev, VFL_TYPE_VIDEO, video_nr) < 0) { v4l2_err(v4l2_dev, "video_register_device failed\n"); goto outvideoreg; diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 2d582c02adbf..e4623ed2f831 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -1214,7 +1214,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, if (saa7134_no_overlay <= 0) dev->video_dev->device_caps |= V4L2_CAP_VIDEO_OVERLAY; - err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, + err = video_register_device(dev->video_dev,VFL_TYPE_VIDEO, video_nr[dev->nr]); if (err < 0) { pr_info("%s: can't register video device\n", diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index cb65d345fd3e..8ad7879bd840 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -291,7 +291,7 @@ static int empress_init(struct saa7134_dev *dev) dev->empress_dev->device_caps |= V4L2_CAP_TUNER; video_set_drvdata(dev->empress_dev, dev); - err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER, + err = video_register_device(dev->empress_dev,VFL_TYPE_VIDEO, empress_nr[dev->nr]); if (err < 0) { pr_info("%s: can't register video device\n", diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c index f96226930670..2214c74bbbf1 100644 --- a/drivers/media/pci/saa7146/hexium_gemini.c +++ b/drivers/media/pci/saa7146/hexium_gemini.c @@ -289,7 +289,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; vv_data.vid_ops.vidioc_g_input = vidioc_g_input; vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER); + ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_VIDEO); if (ret < 0) { pr_err("cannot register capture v4l2 device. skipping.\n"); saa7146_vv_release(dev); diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c index bf5e55348f15..39d14c179d22 100644 --- a/drivers/media/pci/saa7146/hexium_orion.c +++ b/drivers/media/pci/saa7146/hexium_orion.c @@ -362,7 +362,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; vv_data.vid_ops.vidioc_g_input = vidioc_g_input; vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) { + if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_VIDEO)) { pr_err("cannot register capture v4l2 device. skipping.\n"); return -1; } diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index e6a71c17566d..129a1f8ebe1a 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -707,7 +707,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data vv_data.vid_ops.vidioc_g_register = vidioc_g_register; vv_data.vid_ops.vidioc_s_register = vidioc_s_register; #endif - if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { + if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_VIDEO)) { ERR("cannot register capture v4l2 device. skipping.\n"); saa7146_vv_release(dev); return -1; diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c index 3fca7257a720..11e1eb6a6809 100644 --- a/drivers/media/pci/saa7164/saa7164-encoder.c +++ b/drivers/media/pci/saa7164/saa7164-encoder.c @@ -1087,7 +1087,7 @@ int saa7164_encoder_register(struct saa7164_port *port) v4l2_ctrl_handler_setup(hdl); video_set_drvdata(port->v4l_device, port); result = video_register_device(port->v4l_device, - VFL_TYPE_GRABBER, -1); + VFL_TYPE_VIDEO, -1); if (result < 0) { printk(KERN_INFO "%s: can't register mpeg device\n", dev->name); diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 476d7f3b32d6..cbf85231b708 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -1304,7 +1304,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, solo_enc->vfd->queue = &solo_enc->vidq; solo_enc->vfd->lock = &solo_enc->lock; video_set_drvdata(solo_enc->vfd, solo_enc); - ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr); + ret = video_register_device(solo_enc->vfd, VFL_TYPE_VIDEO, nr); if (ret < 0) goto vdev_release; diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c index 78792067e920..54434f3c428d 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -692,7 +692,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) while (erase_off(solo_dev)) /* Do nothing */; - ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr); + ret = video_register_device(solo_dev->vfd, VFL_TYPE_VIDEO, nr); if (ret < 0) goto fail; diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index fd3de3bb0c89..798574cfad35 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1069,7 +1069,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, vip->video_dev.lock = &vip->v4l_lock; video_set_drvdata(&vip->video_dev, vip); - ret = video_register_device(&vip->video_dev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&vip->video_dev, VFL_TYPE_VIDEO, -1); if (ret) goto vrelease; diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c index f3d6c3cdb872..cabe006658dd 100644 --- a/drivers/media/pci/ttpci/av7110_v4l.c +++ b/drivers/media/pci/ttpci/av7110_v4l.c @@ -831,7 +831,7 @@ int av7110_init_v4l(struct av7110 *av7110) if (FW_VERSION(av7110->arm_app) < 0x2623) vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; - if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { + if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_VIDEO)) { ERR("cannot register capture device. skipping\n"); saa7146_vv_release(dev); return -ENODEV; diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c index e2d482af2367..38cac508bd72 100644 --- a/drivers/media/pci/ttpci/budget-av.c +++ b/drivers/media/pci/ttpci/budget-av.c @@ -1470,7 +1470,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio vv_data.vid_ops.vidioc_g_input = vidioc_g_input; vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { + if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_VIDEO))) { /* fixme: proper cleanup here */ ERR("cannot register capture v4l2 device\n"); saa7146_vv_release(dev); diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c index 09732eed7eb4..ec1e06da7e4f 100644 --- a/drivers/media/pci/tw5864/tw5864-video.c +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -1156,7 +1156,7 @@ static int tw5864_video_input_init(struct tw5864_input *input, int video_nr) input->gop = GOP_SIZE; input->frame_interval = 1; - ret = video_register_device(&input->vdev, VFL_TYPE_GRABBER, video_nr); + ret = video_register_device(&input->vdev, VFL_TYPE_VIDEO, video_nr); if (ret) goto free_v4l2_hdl; diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c index 2fb82d50c53e..10986fcd66a5 100644 --- a/drivers/media/pci/tw68/tw68-video.c +++ b/drivers/media/pci/tw68/tw68-video.c @@ -962,7 +962,7 @@ int tw68_video_init2(struct tw68_dev *dev, int video_nr) dev->vdev.lock = &dev->lock; dev->vdev.queue = &dev->vidq; video_set_drvdata(&dev->vdev, dev); - return video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); + return video_register_device(&dev->vdev, VFL_TYPE_VIDEO, video_nr); } /* diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 9be8c6e4fb69..1ced2b0ddb24 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -1282,7 +1282,7 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->device = vdev; video_set_drvdata(vdev, vc); - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (err < 0) goto error; vc->num = vdev->num; From 70cad4495a7438b07d806b8795ab6c6ef083b737 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:18 +0100 Subject: [PATCH 0600/2231] media: media/platform: rename VFL_TYPE_GRABBER to _VIDEO 'GRABBER' is a weird name, all other types map to the /dev device names. Rename to 'VIDEO' to be consistent with the other types. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 2 +- drivers/media/platform/aspeed-video.c | 4 ++-- drivers/media/platform/atmel/atmel-isc-base.c | 2 +- drivers/media/platform/atmel/atmel-isi.c | 2 +- drivers/media/platform/coda/coda-common.c | 2 +- drivers/media/platform/davinci/vpbe_display.c | 2 +- drivers/media/platform/davinci/vpfe_capture.c | 2 +- drivers/media/platform/davinci/vpif_capture.c | 2 +- drivers/media/platform/davinci/vpif_display.c | 2 +- drivers/media/platform/exynos-gsc/gsc-m2m.c | 2 +- drivers/media/platform/exynos4-is/fimc-capture.c | 2 +- drivers/media/platform/exynos4-is/fimc-isp-video.c | 2 +- drivers/media/platform/exynos4-is/fimc-lite.c | 2 +- drivers/media/platform/exynos4-is/fimc-m2m.c | 2 +- drivers/media/platform/fsl-viu.c | 2 +- drivers/media/platform/imx-pxp.c | 2 +- drivers/media/platform/m2m-deinterlace.c | 2 +- drivers/media/platform/marvell-ccic/mcam-core.c | 2 +- drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 2 +- drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c | 2 +- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c | 2 +- drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c | 2 +- drivers/media/platform/mx2_emmaprp.c | 2 +- drivers/media/platform/omap/omap_vout.c | 2 +- drivers/media/platform/omap3isp/ispvideo.c | 4 ++-- drivers/media/platform/pxa_camera.c | 2 +- drivers/media/platform/qcom/camss/camss-video.c | 2 +- drivers/media/platform/qcom/venus/vdec.c | 2 +- drivers/media/platform/qcom/venus/venc.c | 2 +- drivers/media/platform/rcar-vin/rcar-v4l2.c | 2 +- drivers/media/platform/rcar_fdp1.c | 2 +- drivers/media/platform/rcar_jpu.c | 4 ++-- drivers/media/platform/renesas-ceu.c | 2 +- drivers/media/platform/rockchip/rga/rga.c | 2 +- drivers/media/platform/s3c-camif/camif-capture.c | 2 +- drivers/media/platform/s5p-g2d/g2d.c | 2 +- drivers/media/platform/s5p-jpeg/jpeg-core.c | 4 ++-- drivers/media/platform/s5p-mfc/s5p_mfc.c | 4 ++-- drivers/media/platform/sh_veu.c | 2 +- drivers/media/platform/sh_vou.c | 2 +- drivers/media/platform/sti/bdisp/bdisp-v4l2.c | 2 +- drivers/media/platform/sti/delta/delta-v4l2.c | 2 +- drivers/media/platform/sti/hva/hva-v4l2.c | 2 +- drivers/media/platform/stm32/stm32-dcmi.c | 2 +- drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 2 +- drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c | 4 ++-- drivers/media/platform/sunxi/sun8i-di/sun8i-di.c | 2 +- drivers/media/platform/ti-vpe/cal.c | 2 +- drivers/media/platform/ti-vpe/vpe.c | 2 +- drivers/media/platform/via-camera.c | 2 +- drivers/media/platform/vicodec/vicodec-core.c | 2 +- drivers/media/platform/vim2m.c | 2 +- drivers/media/platform/vimc/vimc-capture.c | 2 +- drivers/media/platform/vivid/vivid-core.c | 10 +++++----- drivers/media/platform/vsp1/vsp1_histo.c | 4 ++-- drivers/media/platform/vsp1/vsp1_video.c | 4 ++-- drivers/media/platform/xilinx/xilinx-dma.c | 4 ++-- 57 files changed, 70 insertions(+), 70 deletions(-) diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 09104304bd06..e3f302bc2f96 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2267,7 +2267,7 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; video_set_drvdata(vdev, vpfe); - err = video_register_device(&vpfe->video_dev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&vpfe->video_dev, VFL_TYPE_VIDEO, -1); if (err) { vpfe_err(vpfe, "Unable to register video device.\n"); diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index ef9c09298461..47444a336ebb 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1582,14 +1582,14 @@ static int aspeed_video_setup_video(struct aspeed_video *video) V4L2_CAP_STREAMING; vdev->v4l2_dev = v4l2_dev; strscpy(vdev->name, DEVICE_NAME, sizeof(vdev->name)); - vdev->vfl_type = VFL_TYPE_GRABBER; + vdev->vfl_type = VFL_TYPE_VIDEO; vdev->vfl_dir = VFL_DIR_RX; vdev->release = video_device_release_empty; vdev->ioctl_ops = &aspeed_video_ioctl_ops; vdev->lock = &video->video_lock; video_set_drvdata(vdev, video); - rc = video_register_device(vdev, VFL_TYPE_GRABBER, 0); + rc = video_register_device(vdev, VFL_TYPE_VIDEO, 0); if (rc) { vb2_queue_release(vbq); v4l2_ctrl_handler_free(&video->ctrl_handler); diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index b6c9a78c9198..a6e9797a0ec9 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -2311,7 +2311,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; video_set_drvdata(vdev, isc); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { v4l2_err(&isc->v4l2_dev, "video_register_device failed: %d\n", ret); diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 963dfd6e750e..d74aa73f26be 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1094,7 +1094,7 @@ static int isi_graph_notify_complete(struct v4l2_async_notifier *notifier) return ret; } - ret = video_register_device(isi->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(isi->vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(isi->dev, "Failed to register video device\n"); return ret; diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index acff10ad257a..d0d093dd8f7c 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2726,7 +2726,7 @@ static int coda_register_device(struct coda_dev *dev, int i) v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (!ret) v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", type == CODA_INST_ENCODER ? "encoder" : "decoder", diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index ae419958e420..38d3088d4d38 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1339,7 +1339,7 @@ static int register_device(struct vpbe_layer *vpbe_display_layer, vpbe_display_layer->video_dev.queue = &vpbe_display_layer->buffer_queue; err = video_register_device(&vpbe_display_layer->video_dev, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, -1); if (err) return -ENODEV; diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 9b1d9643589b..5606da559bdf 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1780,7 +1780,7 @@ static int vpfe_probe(struct platform_device *pdev) "video_dev=%p\n", &vpfe_dev->video_dev); vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = video_register_device(&vpfe_dev->video_dev, - VFL_TYPE_GRABBER, -1); + VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(pdev->dev.driver, diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 71f4fe882d13..d9ec439faefa 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1466,7 +1466,7 @@ static int vpif_probe_complete(void) vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; video_set_drvdata(&ch->video_dev, ch); err = video_register_device(vdev, - VFL_TYPE_GRABBER, (j ? 1 : 0)); + VFL_TYPE_VIDEO, (j ? 1 : 0)); if (err) goto probe_out; } diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index abbdbac08e6f..ead14c49d4f5 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1214,7 +1214,7 @@ static int vpif_probe_complete(void) vdev->lock = &common->lock; vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; video_set_drvdata(&ch->video_dev, ch); - err = video_register_device(vdev, VFL_TYPE_GRABBER, + err = video_register_device(vdev, VFL_TYPE_VIDEO, (j ? 3 : 2)); if (err < 0) goto probe_out; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 35a1d0d6dd66..e2c162635f72 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -771,7 +771,7 @@ int gsc_register_m2m_device(struct gsc_dev *gsc) return PTR_ERR(gsc->m2m.m2m_dev); } - ret = video_register_device(&gsc->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&pdev->dev, "%s(): failed to register video device\n", __func__); diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 121d609ff856..705f182330ca 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -1808,7 +1808,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, if (ret) goto err_me_cleanup; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) goto err_ctrl_free; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index d2cbcdca0463..15f443fa7208 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -619,7 +619,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, video_set_drvdata(vdev, isp); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { media_entity_cleanup(&vdev->entity); return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 17de14fbba31..394e0818f2d5 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1297,7 +1297,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) video_set_drvdata(vfd, fimc); fimc->ve.pipe = v4l2_get_subdev_hostdata(sd); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret < 0) { media_entity_cleanup(&vfd->entity); fimc->ve.pipe = NULL; diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index c70c2cbe3eb1..4acb179556c4 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -746,7 +746,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, if (ret) goto err_me; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) goto err_vd; diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 81a8faedbba6..84633a3b8475 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1486,7 +1486,7 @@ static int viu_of_probe(struct platform_device *op) mutex_lock(&viu_dev->lock); - ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(viu_dev->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { video_device_release(viu_dev->vdev); goto err_unlock; diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c index 38d942322302..08d76eb05ed1 100644 --- a/drivers/media/platform/imx-pxp.c +++ b/drivers/media/platform/imx-pxp.c @@ -1709,7 +1709,7 @@ static int pxp_probe(struct platform_device *pdev) goto err_v4l2; } - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_m2m; diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 9ad24c86c5ab..1f89e71cdccf 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -953,7 +953,7 @@ static int deinterlace_probe(struct platform_device *pdev) vfd->lock = &pcdev->dev_mutex; vfd->v4l2_dev = &pcdev->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); goto unreg_dev; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 803baf97f06e..09775b6624c6 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1802,7 +1802,7 @@ static int mccic_notify_bound(struct v4l2_async_notifier *notifier, cam->vdev.lock = &cam->s_mutex; cam->vdev.queue = &cam->vb_queue; video_set_drvdata(&cam->vdev, cam); - ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); if (ret) { cam->sensor = NULL; goto out; diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index ee802fc3bcdf..f82a81a3bdee 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -1150,7 +1150,7 @@ static int mtk_jpeg_probe(struct platform_device *pdev) jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_GRABBER, 3); + ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_VIDEO, 3); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); goto err_dec_vdev_register; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 7c9e2d69e21a..821f2cf325f0 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -1229,7 +1229,7 @@ int mtk_mdp_register_m2m_device(struct mtk_mdp_dev *mdp) goto err_m2m_init; } - ret = video_register_device(mdp->vdev, VFL_TYPE_GRABBER, 2); + ret = video_register_device(mdp->vdev, VFL_TYPE_VIDEO, 2); if (ret) { dev_err(dev, "failed to register video device\n"); goto err_vdev_register; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c index 100ae8c5e702..97a1b6664c20 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c @@ -331,7 +331,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_event_workq; } - ret = video_register_device(vfd_dec, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, 0); if (ret) { mtk_v4l2_err("Failed to register video device"); goto err_dec_reg; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c index 1d82aa2b6017..4d31f1ed113f 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -356,7 +356,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_event_workq; } - ret = video_register_device(vfd_enc, VFL_TYPE_GRABBER, 1); + ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, 1); if (ret) { mtk_v4l2_err("Failed to register video device"); goto err_enc_reg; diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 27779b75df54..df78df59da45 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -866,7 +866,7 @@ static int emmaprp_probe(struct platform_device *pdev) goto rel_vdev; } - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); goto rel_m2m; diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 513b99bf963b..21193f0b7f61 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1500,7 +1500,7 @@ static int __init omap_vout_create_video_devices(struct platform_device *pdev) /* Register the Video device with V4L2 */ vfd = vout->vfd; - if (video_register_device(vfd, VFL_TYPE_GRABBER, -1) < 0) { + if (video_register_device(vfd, VFL_TYPE_VIDEO, -1) < 0) { dev_err(&pdev->dev, ": Could not register Video for Linux device\n"); vfd->minor = -1; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index ee183c35ff3b..0ed250ffd95b 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1453,7 +1453,7 @@ int omap3isp_video_init(struct isp_video *video, const char *name) video->video.fops = &isp_video_fops; snprintf(video->video.name, sizeof(video->video.name), "OMAP3 ISP %s %s", name, direction); - video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.vfl_type = VFL_TYPE_VIDEO; video->video.release = video_device_release_empty; video->video.ioctl_ops = &isp_video_ioctl_ops; if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1484,7 +1484,7 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) video->video.v4l2_dev = vdev; - ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); if (ret < 0) dev_err(video->isp->dev, "%s: could not register video device (%d)\n", diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 321dee9ca81c..70c85a2a10f5 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2191,7 +2191,7 @@ static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier, if (err) goto out_sensor_poweroff; - err = video_register_device(&pcdev->vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&pcdev->vdev, VFL_TYPE_VIDEO, -1); if (err) { v4l2_err(v4l2_dev, "register video device failed: %d\n", err); pcdev->sensor = NULL; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 1d50dfbbb762..c4220778ad47 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -921,7 +921,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, vdev->lock = &video->lock; strscpy(vdev->name, name, sizeof(vdev->name)); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", ret); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 8feaf5daece9..32f0abd4f2fe 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1458,7 +1458,7 @@ static int vdec_probe(struct platform_device *pdev) vdev->v4l2_dev = &core->v4l2_dev; vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto err_vdev_release; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 453edf966d4f..adaa2237fd8f 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1281,7 +1281,7 @@ static int venc_probe(struct platform_device *pdev) vdev->v4l2_dev = &core->v4l2_dev; vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto err_vdev_release; diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index d1b81881c3fa..b12b3f88836a 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -969,7 +969,7 @@ int rvin_v4l2_register(struct rvin_dev *vin) rvin_format_align(vin, &vin->format); - ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&vin->vdev, VFL_TYPE_VIDEO, -1); if (ret) { vin_err(vin, "Failed to register video device\n"); return ret; diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index 97bed45360f0..c9448de885b6 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -2344,7 +2344,7 @@ static int fdp1_probe(struct platform_device *pdev) video_set_drvdata(vfd, fdp1); strscpy(vfd->name, fdp1_videodev.name, sizeof(vfd->name)); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&fdp1->v4l2_dev, "Failed to register video device\n"); goto release_m2m; diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 1c3f507acfc9..5250a14324e9 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1663,7 +1663,7 @@ static int jpu_probe(struct platform_device *pdev) jpu->vfd_encoder.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); goto m2m_init_rollback; @@ -1682,7 +1682,7 @@ static int jpu_probe(struct platform_device *pdev) jpu->vfd_decoder.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); goto enc_vdev_register_rollback; diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index 197b3991330d..f7d71a6a7970 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -1450,7 +1450,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier) V4L2_CAP_STREAMING; video_set_drvdata(vdev, ceudev); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { v4l2_err(vdev->v4l2_dev, "video_register_device failed: %d\n", ret); diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e9ff12b6b5bb..9d122429706e 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -889,7 +889,7 @@ static int rga_probe(struct platform_device *pdev) def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; def_frame.size = def_frame.stride * def_frame.height; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); goto rel_vdev; diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 2fb45db8e4ba..9ca49af29542 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1158,7 +1158,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) vfd->ctrl_handler = &vp->ctrl_handler; vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) goto err_ctrlh_free; diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index f5f05ea9f521..6932fd47071b 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -695,7 +695,7 @@ static int g2d_probe(struct platform_device *pdev) vfd->lock = &dev->mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto rel_vdev; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index ac2162235cef..86bda3947110 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2946,7 +2946,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_encoder->vfl_dir = VFL_DIR_M2M; jpeg->vfd_encoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; - ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); + ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); video_device_release(jpeg->vfd_encoder); @@ -2976,7 +2976,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_decoder->vfl_dir = VFL_DIR_M2M; jpeg->vfd_decoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; - ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1); + ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); video_device_release(jpeg->vfd_decoder); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index b776f83e395e..5c2a23b953a4 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1376,7 +1376,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) s5p_mfc_init_regs(dev); /* Register decoder and encoder */ - ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0); + ret = video_register_device(dev->vfd_dec, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_dec_reg; @@ -1384,7 +1384,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) v4l2_info(&dev->v4l2_dev, "decoder registered as /dev/video%d\n", dev->vfd_dec->num); - ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0); + ret = video_register_device(dev->vfd_enc, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_enc_reg; diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 2b4c0d9d6928..f08b8fc192d8 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -1160,7 +1160,7 @@ static int sh_veu_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); pm_runtime_suspend(&pdev->dev); if (ret < 0) goto evidreg; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 2236702c21b4..36e5f2ff4ef1 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1323,7 +1323,7 @@ static int sh_vou_probe(struct platform_device *pdev) goto ei2cnd; } - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) goto evregdev; diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index d1025a53709f..af2d5eb782ce 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -1066,7 +1066,7 @@ static int bdisp_register_device(struct bdisp_dev *bdisp) return PTR_ERR(bdisp->m2m.m2m_dev); } - ret = video_register_device(&bdisp->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&bdisp->vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(bdisp->dev, "%s(): failed to register video device\n", __func__); diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/sti/delta/delta-v4l2.c index 91369fb3ffaa..2503224eeee5 100644 --- a/drivers/media/platform/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/sti/delta/delta-v4l2.c @@ -1781,7 +1781,7 @@ static int delta_register_device(struct delta_dev *delta) snprintf(vdev->name, sizeof(vdev->name), "%s-%s", DELTA_NAME, DELTA_FW_VERSION); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(delta->dev, "%s failed to register video device\n", DELTA_PREFIX); diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 64004d15a9c9..197b99d8fd9c 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -1316,7 +1316,7 @@ static int hva_register_device(struct hva_dev *hva) snprintf(vdev->name, sizeof(vdev->name), "%s%lx", HVA_NAME, hva->ip_version); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(dev, "%s failed to register video device\n", HVA_PREFIX); diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index b63ff8d636ce..b8931490b83b 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1974,7 +1974,7 @@ static int dcmi_probe(struct platform_device *pdev) } dcmi->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; - ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(dcmi->vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(dcmi->dev, "Failed to register video device\n"); goto err_media_entity_cleanup; diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 83a3a0257c7b..5aee3fd875ad 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -374,7 +374,7 @@ int sun4i_csi_v4l2_register(struct sun4i_csi *csi) vdev->ioctl_ops = &sun4i_csi_ioctl_ops; video_set_drvdata(vdev, csi); - ret = video_register_device(&csi->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&csi->vdev, VFL_TYPE_VIDEO, -1); if (ret) return ret; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index f0dfe68486d1..cb30efb5d1f1 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -648,7 +648,7 @@ int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi, vdev->release = video_device_release_empty; vdev->fops = &sun6i_video_fops; vdev->ioctl_ops = &sun6i_video_ioctl_ops; - vdev->vfl_type = VFL_TYPE_GRABBER; + vdev->vfl_type = VFL_TYPE_VIDEO; vdev->vfl_dir = VFL_DIR_RX; vdev->v4l2_dev = &csi->v4l2_dev; vdev->queue = vidq; @@ -656,7 +656,7 @@ int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi, vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; video_set_drvdata(vdev, video); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { v4l2_err(&csi->v4l2_dev, "video_register_device failed: %d\n", ret); diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index b61f3dea7c93..214567367324 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -882,7 +882,7 @@ static int deinterlace_probe(struct platform_device *pdev) deinterlace_video_device.name); video_set_drvdata(vfd, dev); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index be54806180a5..6e009e479be3 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1948,7 +1948,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx) vfd->lock = &ctx->mutex; video_set_drvdata(vfd, ctx); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); if (ret < 0) return ret; diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 65c2c048b018..cff2fcd6d812 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2500,7 +2500,7 @@ static void vpe_fw_cb(struct platform_device *pdev) vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { vpe_err(dev, "Failed to register video device\n"); diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index 78841b9015ce..c88fd3403eda 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -1262,7 +1262,7 @@ static int viacam_probe(struct platform_device *pdev) cam->vdev.lock = &cam->lock; cam->vdev.queue = vq; video_set_drvdata(&cam->vdev, cam); - ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); if (ret) goto out_irq; diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 82350097503e..f78579b48762 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -2120,7 +2120,7 @@ static int register_instance(struct vicodec_dev *dev, } video_set_drvdata(vfd, dev); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device '%s'\n", name); v4l2_m2m_release(dev_instance->m2m_dev); diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 8d6b09623d88..ac6717fbb764 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1333,7 +1333,7 @@ static int vim2m_probe(struct platform_device *pdev) vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto error_v4l2; diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 76c015898cfd..0615aa4dc733 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -460,7 +460,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, video_set_drvdata(vdev, &vcap->ved); /* Register the video_device with the v4l2 and the media framework */ - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&vimc->pdev.dev, "%s: video register failed (err=%d)\n", vcap->vdev.name, ret); diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 15091cbf6de7..c62c068b6b60 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -407,7 +407,7 @@ static int vidioc_log_status(struct file *file, void *fh) struct video_device *vdev = video_devdata(file); v4l2_ctrl_log_status(file, fh); - if (vdev->vfl_dir == VFL_DIR_RX && vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_dir == VFL_DIR_RX && vdev->vfl_type == VFL_TYPE_VIDEO) tpg_log_status(&dev->tpg); return 0; } @@ -1525,7 +1525,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } #endif - ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, vid_cap_nr[inst]); if (ret < 0) goto unreg_dev; v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", @@ -1578,7 +1578,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } #endif - ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, vid_out_nr[inst]); if (ret < 0) goto unreg_dev; v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n", @@ -1734,7 +1734,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (ret) goto unreg_dev; #endif - ret = video_register_device(vfd, VFL_TYPE_GRABBER, + ret = video_register_device(vfd, VFL_TYPE_VIDEO, meta_cap_nr[inst]); if (ret < 0) goto unreg_dev; @@ -1764,7 +1764,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (ret) goto unreg_dev; #endif - ret = video_register_device(vfd, VFL_TYPE_GRABBER, + ret = video_register_device(vfd, VFL_TYPE_VIDEO, meta_out_nr[inst]); if (ret < 0) goto unreg_dev; diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/vsp1/vsp1_histo.c index 30d751f2cccf..a91e142bcb94 100644 --- a/drivers/media/platform/vsp1/vsp1_histo.c +++ b/drivers/media/platform/vsp1/vsp1_histo.c @@ -551,7 +551,7 @@ int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo, histo->video.fops = &histo_v4l2_fops; snprintf(histo->video.name, sizeof(histo->video.name), "%s histo", histo->entity.subdev.name); - histo->video.vfl_type = VFL_TYPE_GRABBER; + histo->video.vfl_type = VFL_TYPE_VIDEO; histo->video.release = video_device_release_empty; histo->video.ioctl_ops = &histo_v4l2_ioctl_ops; histo->video.device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; @@ -576,7 +576,7 @@ int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo, /* ... and register the video device. */ histo->video.queue = &histo->queue; - ret = video_register_device(&histo->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&histo->video, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(vsp1->dev, "failed to register video device\n"); goto error; diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 5e59ed2c3614..044eb5778820 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -1293,7 +1293,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video->video.fops = &vsp1_video_fops; snprintf(video->video.name, sizeof(video->video.name), "%s %s", rwpf->entity.subdev.name, direction); - video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.vfl_type = VFL_TYPE_VIDEO; video->video.release = video_device_release_empty; video->video.ioctl_ops = &vsp1_video_ioctl_ops; @@ -1316,7 +1316,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, /* ... and register the video device. */ video->video.queue = &video->queue; - ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(video->vsp1->dev, "failed to register video device\n"); goto error; diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 3bb54a4db6a4..2a56201cb853 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -685,7 +685,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, xdev->dev->of_node, type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input", port); - dma->video.vfl_type = VFL_TYPE_GRABBER; + dma->video.vfl_type = VFL_TYPE_VIDEO; dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? VFL_DIR_RX : VFL_DIR_TX; dma->video.release = video_device_release_empty; @@ -735,7 +735,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, dma->align = 1 << dma->dma->device->copy_align; - ret = video_register_device(&dma->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&dma->video, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(dma->xdev->dev, "failed to register video device\n"); goto error; From dc42cf74d98a39af30ecaf78e7fad7e265ae528f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:19 +0100 Subject: [PATCH 0601/2231] media: v4l2-dev.h: remove VFL_TYPE_GRABBER The last driver was converted to use VFL_TYPE_VIDEO, so the old _GRABBER enum can now be removed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-dev.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 5e7c0f8acd05..4602c15ff878 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -25,7 +25,6 @@ * enum vfl_devnode_type - type of V4L2 device node * * @VFL_TYPE_VIDEO: for video input/output devices - * @VFL_TYPE_GRABBER: deprecated, same as VFL_TYPE_VIDEO * @VFL_TYPE_VBI: for vertical blank data (i.e. closed captions, teletext) * @VFL_TYPE_RADIO: for radio tuners * @VFL_TYPE_SUBDEV: for V4L2 subdevices @@ -35,7 +34,6 @@ */ enum vfl_devnode_type { VFL_TYPE_VIDEO, - VFL_TYPE_GRABBER = VFL_TYPE_VIDEO, VFL_TYPE_VBI, VFL_TYPE_RADIO, VFL_TYPE_SUBDEV, From 6821660c4386e31e9344fb8dc87111ffbc9cb60b Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 16 Jan 2020 15:36:30 +0100 Subject: [PATCH 0602/2231] media: sun8i: Remove redundant platform_get_irq error message platform_get_irq() will call dev_err() itself on failure, so there is no need for the driver to also do this. This is detected by coccinelle. Signed-off-by: YueHaibing Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sunxi/sun8i-di/sun8i-di.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index 214567367324..d78f6593ddd1 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -814,11 +814,8 @@ static int deinterlace_probe(struct platform_device *pdev) dev->dev = &pdev->dev; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev->dev, "Failed to get IRQ\n"); - + if (irq <= 0) return irq; - } ret = devm_request_irq(dev->dev, irq, deinterlace_irq, 0, dev_name(dev->dev), dev); From 5dbef6b3e09ffabf3b74d8d0fb230179d8fdc840 Mon Sep 17 00:00:00 2001 From: Cengiz Can Date: Thu, 16 Jan 2020 16:12:30 +0100 Subject: [PATCH 0603/2231] media: davinci: fix incorrect pix_fmt assignment There's a mistakenly written self assignment in `static int vpfe_enum_fmt_vid_cap(..)`. Fixed it according to Prabhakar Lad's feedback. Signed-off-by: Cengiz Can Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpfe_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 5606da559bdf..f9f7dd17c57c 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -880,7 +880,7 @@ static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, /* Fill in the information about format */ pix_fmt = vpfe_lookup_pix_format(pix); if (pix_fmt) { - fmt->pixelformat = fmt->pixelformat; + fmt->pixelformat = pix_fmt->pixelformat; return 0; } return -EINVAL; From 3eed7385bff6fc20edbe77edce3940b6cc3bf134 Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Fri, 17 Jan 2020 21:12:18 +0100 Subject: [PATCH 0604/2231] media: staging: media: rkisp1: make links immutable by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only places which make sese to allow users to enable or disable links are: * between sensors and isp: So users can select which sensor should be used while streaming * between isp and the resizers: | v here rkisp1_isp:2 -> rkisp1_resizer_mainpath -> rkisp1_mainpath (capture) \-> rkisp1_resizer_selfpath -> rkisp1_selfpath (capture) ^ here | So users can disable one of the capture paths when unused, to avoid worring about matching formats. Make the following links immutable to simplify userspace: rkisp1_resizer_mainpath -> rkisp1_mainpath rkisp1_resizer_selfpath -> rkisp1_selfpath rkisp1_params -> rkisp1_isp rkisp1_isp -> rkisp1_stats Signed-off-by: Helen Koike Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-dev.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 558126e66465..4030d5e71af1 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -145,14 +145,15 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) flags = 0; } - flags = MEDIA_LNK_FL_ENABLED; + flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; /* create ISP->RSZ->CAP links */ for (i = 0; i < 2; i++) { source = &rkisp1->isp.sd.entity; sink = &rkisp1->resizer_devs[i].sd.entity; ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_VIDEO, - sink, RKISP1_RSZ_PAD_SINK, flags); + sink, RKISP1_RSZ_PAD_SINK, + MEDIA_LNK_FL_ENABLED); if (ret) return ret; From 8b9f91751b6ff19449526d63a0675177ce10f9c7 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Wed, 22 Jan 2020 10:52:51 +0100 Subject: [PATCH 0605/2231] media: vimc: streamer: if kthread_stop fails, ignore the error Ignore errors returned from kthread_stop since the vimc subdevices should still be notified that streaming stopped so they can release the memory for the streaming, and also kthread should be set to NULL. kthread_stop can return -EINTR in case the thread did not yet run. This can happen if userspace calls streamon and streamoff right after. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-streamer.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c index 43e494df61d8..65feb3c596db 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -220,8 +220,14 @@ int vimc_streamer_s_stream(struct vimc_stream *stream, return 0; ret = kthread_stop(stream->kthread); + /* + * kthread_stop returns -EINTR in cases when streamon was + * immediately followed by streamoff, and the thread didn't had + * a chance to run. Ignore errors to stop the stream in the + * pipeline. + */ if (ret) - return ret; + dev_dbg(ved->dev, "kthread_stop returned '%d'\n", ret); stream->kthread = NULL; From 26e1ff53640d65a0c3e4ad81a9bedf86b7f5de9f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 23 Jan 2020 02:06:43 +0100 Subject: [PATCH 0606/2231] media: allegro: fix spelling mistake "to" -> "too" There is a spelling mistake in a v4l2_err message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Michael Tretter Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/allegro-dvt/allegro-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index ef53730fff6b..3c949090e8d2 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -781,7 +781,7 @@ static int allegro_mbox_write(struct allegro_dev *dev, if (size > mbox->size) { v4l2_err(&dev->v4l2_dev, - "message (%zu bytes) to large for mailbox (%zu bytes)\n", + "message (%zu bytes) too large for mailbox (%zu bytes)\n", size, mbox->size); return -EINVAL; } From e34bca49e4953e5c2afc0425303199a5fd515f82 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 27 Jan 2020 15:30:06 +0100 Subject: [PATCH 0607/2231] media: hantro: Read be32 words starting at every fourth byte Since (luma/chroma)_qtable is an array of unsigned char, indexing it returns consecutive byte locations, but we are supposed to read the arrays in four-byte words. Consequently, we should be pointing get_unaligned_be32() at consecutive word locations instead. Signed-off-by: Andrzej Pietrasiewicz Reviewed-by: Ezequiel Garcia Tested-by: Ezequiel Garcia Cc: stable@vger.kernel.org Fixes: 00c30f42c7595f "media: rockchip vpu: remove some unused vars" Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_h1_jpeg_enc.c | 9 +++++++-- drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index 0d8afc3e5d71..4f72d92cd98f 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -67,12 +67,17 @@ hantro_h1_jpeg_enc_set_qtable(struct hantro_dev *vpu, unsigned char *chroma_qtable) { u32 reg, i; + __be32 *luma_qtable_p; + __be32 *chroma_qtable_p; + + luma_qtable_p = (__be32 *)luma_qtable; + chroma_qtable_p = (__be32 *)chroma_qtable; for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&luma_qtable[i]); + reg = get_unaligned_be32(&luma_qtable_p[i]); vepu_write_relaxed(vpu, reg, H1_REG_JPEG_LUMA_QUAT(i)); - reg = get_unaligned_be32(&chroma_qtable[i]); + reg = get_unaligned_be32(&chroma_qtable_p[i]); vepu_write_relaxed(vpu, reg, H1_REG_JPEG_CHROMA_QUAT(i)); } } diff --git a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c index 4c2d43fb6fd1..a85c4f9fd10a 100644 --- a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c @@ -98,12 +98,17 @@ rk3399_vpu_jpeg_enc_set_qtable(struct hantro_dev *vpu, unsigned char *chroma_qtable) { u32 reg, i; + __be32 *luma_qtable_p; + __be32 *chroma_qtable_p; + + luma_qtable_p = (__be32 *)luma_qtable; + chroma_qtable_p = (__be32 *)chroma_qtable; for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&luma_qtable[i]); + reg = get_unaligned_be32(&luma_qtable_p[i]); vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(i)); - reg = get_unaligned_be32(&chroma_qtable[i]); + reg = get_unaligned_be32(&chroma_qtable_p[i]); vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_CHROMA_QUAT(i)); } } From 801fccf48940c6733aad922f7b9e81f6d7f84cf6 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 27 Jan 2020 15:30:07 +0100 Subject: [PATCH 0608/2231] media: hantro: Use standard luma quantization table The table is actually different in the document than in this file, so align this file with the document. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_jpeg.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/hantro/hantro_jpeg.c b/drivers/staging/media/hantro/hantro_jpeg.c index 125eb41f2ede..d3b381d00b23 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.c +++ b/drivers/staging/media/hantro/hantro_jpeg.c @@ -23,17 +23,17 @@ #define HUFF_CHROMA_AC_OFF 409 /* Default tables from JPEG ITU-T.81 - * (ISO/IEC 10918-1) Annex K.3, I + * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 */ static const unsigned char luma_q_table[] = { - 0x10, 0x0b, 0x0a, 0x10, 0x7c, 0x8c, 0x97, 0xa1, - 0x0c, 0x0c, 0x0e, 0x13, 0x7e, 0x9e, 0xa0, 0x9b, - 0x0e, 0x0d, 0x10, 0x18, 0x8c, 0x9d, 0xa9, 0x9c, - 0x0e, 0x11, 0x16, 0x1d, 0x97, 0xbb, 0xb4, 0xa2, - 0x12, 0x16, 0x25, 0x38, 0xa8, 0x6d, 0x67, 0xb1, - 0x18, 0x23, 0x37, 0x40, 0xb5, 0x68, 0x71, 0xc0, + 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, + 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, + 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, + 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, + 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, + 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, - 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0xc7, + 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 }; static const unsigned char chroma_q_table[] = { From 85bdcb7eaae7c39dba9420de6df1cb227cf05732 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 27 Jan 2020 15:30:08 +0100 Subject: [PATCH 0609/2231] media: hantro: Write the quantization tables in proper order The quantization tables as defined in the file (luma_q_table, chroma_q_table) are in fact in linear order. The JPEG file header, which is not generated by the hardware, but must be programatically created with the CPU, expects the table in zigzag order. On the other hand, the hardware doesn't expect neither linear, nor zigzag order. Instead it expects the quantization tables in vertical groups of four quantization parameters, and the groups are organized in blocks of two vertically adjacent groups. On top of that the blocks must be provided to the hardware in this order: leftmost top block, leftmost bottom block, second leftmost top block, second leftmost bottom block and so on. So, if this is the quantization table in linear order: 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 then the hardware expects this in its consecutive registers: 0x100c0e0e, 0x0b0c0d11, 0x12183148, 0x1623405c, 0x0a0e1016, 0x1013181d, 0x25374e5f, 0x38405762, and so on. Consequently, the same area of memory cannot be used both for dumping it into the JPEG file header and writing its contents to the hardware registers. Instead, a separate pair of arrays is added for properly reordered quantization tables, to be read with get_unaligned_be32() and linearly written to the registers. The "ctx" parameter is not needed any more for hantro_jpeg_get_qtable(). Signed-off-by: Andrzej Pietrasiewicz Tested-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/hantro/hantro_h1_jpeg_enc.c | 4 +- drivers/staging/media/hantro/hantro_jpeg.c | 62 +++++++++++++++---- drivers/staging/media/hantro/hantro_jpeg.h | 2 +- .../media/hantro/rk3399_vpu_hw_jpeg_enc.c | 9 ++- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index 4f72d92cd98f..f62ab96078c6 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -108,8 +108,8 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) hantro_h1_set_src_img_ctrl(vpu, ctx); hantro_h1_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); hantro_h1_jpeg_enc_set_qtable(vpu, - hantro_jpeg_get_qtable(&jpeg_ctx, 0), - hantro_jpeg_get_qtable(&jpeg_ctx, 1)); + hantro_jpeg_get_qtable(0), + hantro_jpeg_get_qtable(1)); reg = H1_REG_AXI_CTRL_OUTPUT_SWAP16 | H1_REG_AXI_CTRL_INPUT_SWAP16 diff --git a/drivers/staging/media/hantro/hantro_jpeg.c b/drivers/staging/media/hantro/hantro_jpeg.c index d3b381d00b23..36c140fc6a36 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.c +++ b/drivers/staging/media/hantro/hantro_jpeg.c @@ -36,6 +36,8 @@ static const unsigned char luma_q_table[] = { 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 }; +static unsigned char luma_q_table_reordered[ARRAY_SIZE(luma_q_table)]; + static const unsigned char chroma_q_table[] = { 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, @@ -47,6 +49,30 @@ static const unsigned char chroma_q_table[] = { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 }; +static unsigned char chroma_q_table_reordered[ARRAY_SIZE(chroma_q_table)]; + +static const unsigned char zigzag[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static const u32 hw_reorder[64] = { + 0, 8, 16, 24, 1, 9, 17, 25, + 32, 40, 48, 56, 33, 41, 49, 57, + 2, 10, 18, 26, 3, 11, 19, 27, + 34, 42, 50, 58, 35, 43, 51, 59, + 4, 12, 20, 28, 5, 13, 21, 29, + 36, 44, 52, 60, 37, 45, 53, 61, + 6, 14, 22, 30, 7, 15, 23, 31, + 38, 46, 54, 62, 39, 47, 55, 63 +}; + /* Huffman tables are shared with CODA */ static const unsigned char luma_dc_table[] = { 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, @@ -225,20 +251,29 @@ static const unsigned char hantro_jpeg_header[JPEG_HEADER_SIZE] = { 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, }; -static void -jpeg_scale_quant_table(unsigned char *q_tab, - const unsigned char *tab, int scale) +static unsigned char jpeg_scale_qp(const unsigned char qp, int scale) { unsigned int temp; + + temp = DIV_ROUND_CLOSEST((unsigned int)qp * scale, 100); + if (temp <= 0) + temp = 1; + if (temp > 255) + temp = 255; + + return (unsigned char)temp; +} + +static void +jpeg_scale_quant_table(unsigned char *file_q_tab, + unsigned char *reordered_q_tab, + const unsigned char *tab, int scale) +{ int i; for (i = 0; i < 64; i++) { - temp = DIV_ROUND_CLOSEST((unsigned int)tab[i] * scale, 100); - if (temp <= 0) - temp = 1; - if (temp > 255) - temp = 255; - q_tab[i] = (unsigned char)temp; + file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); + reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); } } @@ -256,17 +291,18 @@ static void jpeg_set_quality(unsigned char *buffer, int quality) scale = 200 - 2 * quality; jpeg_scale_quant_table(buffer + LUMA_QUANT_OFF, + luma_q_table_reordered, luma_q_table, scale); jpeg_scale_quant_table(buffer + CHROMA_QUANT_OFF, + chroma_q_table_reordered, chroma_q_table, scale); } -unsigned char * -hantro_jpeg_get_qtable(struct hantro_jpeg_ctx *ctx, int index) +unsigned char *hantro_jpeg_get_qtable(int index) { if (index == 0) - return ctx->buffer + LUMA_QUANT_OFF; - return ctx->buffer + CHROMA_QUANT_OFF; + return luma_q_table_reordered; + return chroma_q_table_reordered; } void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) diff --git a/drivers/staging/media/hantro/hantro_jpeg.h b/drivers/staging/media/hantro/hantro_jpeg.h index 9e8397c71388..9474a00277f8 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.h +++ b/drivers/staging/media/hantro/hantro_jpeg.h @@ -9,5 +9,5 @@ struct hantro_jpeg_ctx { unsigned char *buffer; }; -unsigned char *hantro_jpeg_get_qtable(struct hantro_jpeg_ctx *ctx, int index); +unsigned char *hantro_jpeg_get_qtable(int index); void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx); diff --git a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c index a85c4f9fd10a..d248979908c3 100644 --- a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c @@ -18,9 +18,8 @@ * * Quantization luma table values are written to registers * VEPU_swreg_0-VEPU_swreg_15, and chroma table values to - * VEPU_swreg_16-VEPU_swreg_31. - * - * JPEG zigzag order is expected on the quantization tables. + * VEPU_swreg_16-VEPU_swreg_31. A special order is needed, neither + * zigzag, nor linear. */ #include @@ -139,8 +138,8 @@ void rk3399_vpu_jpeg_enc_run(struct hantro_ctx *ctx) rk3399_vpu_set_src_img_ctrl(vpu, ctx); rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3399_vpu_jpeg_enc_set_qtable(vpu, - hantro_jpeg_get_qtable(&jpeg_ctx, 0), - hantro_jpeg_get_qtable(&jpeg_ctx, 1)); + hantro_jpeg_get_qtable(0), + hantro_jpeg_get_qtable(1)); reg = VEPU_REG_OUTPUT_SWAP32 | VEPU_REG_OUTPUT_SWAP16 From 1b3bb574569e5fe5aeb0a9c73848430b7e271c20 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 27 Jan 2020 15:30:09 +0100 Subject: [PATCH 0610/2231] media: hantro: Write quantization table registers in increasing addresses order Luma and chroma qtables need to be written into two 16-register blocks, each table consisting of 64 bytes total. The blocks are contiguous and start at offset 0 for luma and at offset 0x40 for chroma. The seemingly innocent optimization of writing the two blocks using one loop causes side effects which result in improper values of quantization tables being used by the hardware during encoding. Visually this results in macroblocking artifacts around contrasting edges in encoded images. The artifacts look like horizontally flipped shadows of the said edges. Changing the write operations to non-relaxed variant doesn't help. This patch removes this premature optimization and after this change the macroblocking artifacts around contrasting edges are gone. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_h1_jpeg_enc.c | 6 ++++++ drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index f62ab96078c6..b22418436823 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -73,10 +73,16 @@ hantro_h1_jpeg_enc_set_qtable(struct hantro_dev *vpu, luma_qtable_p = (__be32 *)luma_qtable; chroma_qtable_p = (__be32 *)chroma_qtable; + /* + * Quantization table registers must be written in contiguous blocks. + * DO NOT collapse the below two "for" loops into one. + */ for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { reg = get_unaligned_be32(&luma_qtable_p[i]); vepu_write_relaxed(vpu, reg, H1_REG_JPEG_LUMA_QUAT(i)); + } + for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { reg = get_unaligned_be32(&chroma_qtable_p[i]); vepu_write_relaxed(vpu, reg, H1_REG_JPEG_CHROMA_QUAT(i)); } diff --git a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c index d248979908c3..3498e6124acd 100644 --- a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c @@ -103,10 +103,16 @@ rk3399_vpu_jpeg_enc_set_qtable(struct hantro_dev *vpu, luma_qtable_p = (__be32 *)luma_qtable; chroma_qtable_p = (__be32 *)chroma_qtable; + /* + * Quantization table registers must be written in contiguous blocks. + * DO NOT collapse the below two "for" loops into one. + */ for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { reg = get_unaligned_be32(&luma_qtable_p[i]); vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(i)); + } + for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { reg = get_unaligned_be32(&chroma_qtable_p[i]); vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_CHROMA_QUAT(i)); } From 042584e9055b615ac917239884fb0d65690f56ec Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 27 Jan 2020 15:56:02 +0100 Subject: [PATCH 0611/2231] media: hantro: fix extra MV/MC sync space calculation Add space for MVs and MC sync data to the capture buffers depending on whether the post processor will be enabled for the new capture format passed to TRY_FMT, not the currently set capture format. Signed-off-by: Philipp Zabel Reviewed-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro_v4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index 0198bcda26b7..f4ae2cee0f18 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -295,7 +295,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f, * +---------------------------+ */ if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE && - !hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + !hantro_needs_postproc(ctx, fmt)) pix_mp->plane_fmt[0].sizeimage += 64 * MB_WIDTH(pix_mp->width) * MB_WIDTH(pix_mp->height) + 32; From 986eee3a5234fa43b94928f63b9fd8b513cd96e7 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 29 Jan 2020 22:06:08 +0100 Subject: [PATCH 0612/2231] media: hantro: Prevent encoders from using post-processing The post-processing feature is meant to be used by decoding only. Prevent encoding jobs from enabling it. Fixes: 8c2d66b036c77 ("media: hantro: Support color conversion via post-processing") Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/hantro/hantro.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index b0faa43b3f79..327ddef45345 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -423,7 +423,7 @@ hantro_get_dst_buf(struct hantro_ctx *ctx) static inline bool hantro_needs_postproc(struct hantro_ctx *ctx, const struct hantro_fmt *fmt) { - return fmt->fourcc != V4L2_PIX_FMT_NV12; + return !hantro_is_encoder_ctx(ctx) && fmt->fourcc != V4L2_PIX_FMT_NV12; } static inline dma_addr_t From 0c9d29eb1cceb8a155dd42769cc88b50c45f0dbe Mon Sep 17 00:00:00 2001 From: Justin Swartz Date: Mon, 3 Feb 2020 23:40:15 +0100 Subject: [PATCH 0613/2231] media: dt-bindings: Add binding for rk3228 rga Indicate that the rk3228 rga is compatible with that of the rk3288. But if any rk3228-specific quirks are identified in future that require handling logic that differs from what is provided for the rk3288, then allow for the compatibility string "rockchip,rk3228-rga" to be matched instead of "rockchip,rk3288-rga". Signed-off-by: Justin Swartz Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rockchip-rga.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rockchip-rga.txt b/Documentation/devicetree/bindings/media/rockchip-rga.txt index fd5276abfad6..c53a8e5133f6 100644 --- a/Documentation/devicetree/bindings/media/rockchip-rga.txt +++ b/Documentation/devicetree/bindings/media/rockchip-rga.txt @@ -6,8 +6,9 @@ BitBLT, alpha blending and image blur/sharpness. Required properties: - compatible: value should be one of the following - "rockchip,rk3288-rga"; - "rockchip,rk3399-rga"; + "rockchip,rk3228-rga", "rockchip,rk3288-rga": for Rockchip RK3228 + "rockchip,rk3288-rga": for Rockchip RK3288 + "rockchip,rk3399-rga": for Rockchip RK3399 - interrupts: RGA interrupt specifier. From a3fbc2e6bb05a3b1ea341cd29dea09b4a033727b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Feb 2020 19:13:13 +0100 Subject: [PATCH 0614/2231] media: mc-entity.c: use WARN_ON, validate link pads Use WARN_ON instead of BUG_ON. Add two new WARN_ONs to verify that the source pad is really a source and that the sink pad is really a sink. Signed-off-by: Hans Verkuil [hverkuil-cisco@xs4all.nl: use ! instead of == NULL for source and sink] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/mc/mc-entity.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c index 668770e9f609..211279c5fd77 100644 --- a/drivers/media/mc/mc-entity.c +++ b/drivers/media/mc/mc-entity.c @@ -662,9 +662,14 @@ media_create_pad_link(struct media_entity *source, u16 source_pad, struct media_link *link; struct media_link *backlink; - BUG_ON(source == NULL || sink == NULL); - BUG_ON(source_pad >= source->num_pads); - BUG_ON(sink_pad >= sink->num_pads); + if (WARN_ON(!source || !sink) || + WARN_ON(source_pad >= source->num_pads) || + WARN_ON(sink_pad >= sink->num_pads)) + return -EINVAL; + if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE))) + return -EINVAL; + if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) + return -EINVAL; link = media_add_link(&source->links); if (link == NULL) From fdab089e3150d92b2409994fdc32350dbfefab85 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 4 Feb 2020 22:44:44 +0100 Subject: [PATCH 0615/2231] media: staging: rkisp1: don't lock media's graph_mutex when calling rkisp1_create_links The function rkisp1_create_links calls media_entity_get_fwnode_pad and media_create_pad_link in the mc api. Those calls don't require locking the graph_mutex so remove the locking. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-dev.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 4030d5e71af1..275310d69ca1 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -220,19 +220,17 @@ static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier) container_of(notifier, struct rkisp1_device, notifier); int ret; - mutex_lock(&rkisp1->media_dev.graph_mutex); ret = rkisp1_create_links(rkisp1); if (ret) - goto unlock; + return ret; + ret = v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev); if (ret) - goto unlock; + return ret; dev_dbg(rkisp1->dev, "Async subdev notifier completed\n"); -unlock: - mutex_unlock(&rkisp1->media_dev.graph_mutex); - return ret; + return 0; } static int rkisp1_fwnode_parse(struct device *dev, From 0564fd36f6f54ed552de5a01331bd36168507841 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 4 Feb 2020 22:44:45 +0100 Subject: [PATCH 0616/2231] media: staging: rkisp1: fix test of return value of media_entity_get_fwnode_pad media_entity_get_fwnode_pad returns negative value on error and the pad numeber on success. Therefore change the error test from 'if (ret)' to 'if (ret < 0)' . Fixes: d65dd85281fb ("media: staging: rkisp1: add Rockchip ISP1 base driver") Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 275310d69ca1..0f74153ce66b 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -128,7 +128,7 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode, MEDIA_PAD_FL_SOURCE); - if (ret) { + if (ret < 0) { dev_err(sd->dev, "failed to find src pad for %s\n", sd->name); return ret; From 2f43d268f984eb859bb4c3e3ad3f824f4963f857 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Tue, 4 Feb 2020 22:44:46 +0100 Subject: [PATCH 0617/2231] media: staging: rkisp1: improve inner documentation in rkisp1-isp.c Improve the documentation in the beginning of the file rkisp1-isp1.c Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-isp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 328c7ea60971..7b1a5909a59e 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -28,9 +28,9 @@ #define RKISP1_DIR_SINK_SRC (RKISP1_DIR_SINK | RKISP1_DIR_SRC) /* - * NOTE: MIPI controller and input MUX are also configured in this file, - * because ISP Subdev is not only describe ISP submodule(input size,format, - * output size, format), but also a virtual route device. + * NOTE: MIPI controller and input MUX are also configured in this file. + * This is because ISP Subdev describes not only ISP submodule (input size, + * format, output size, format), but also a virtual route device. */ /* From 12d3d8090bc5e8cdda2f56caed2a2a0d70009456 Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Thu, 6 Feb 2020 23:07:08 +0100 Subject: [PATCH 0618/2231] media: staging: rkisp1: use consistent bus_info string for media_dev Media device is using a slightly different bus_info string "platform: rkisp1" (with a space) instead of "platform:rkisp1" used by the rest of rkisp1 code. This causes errors when using v4l2-util tools that uses the bus_info string to identify the device. Fixes: d65dd85281fb ("media: staging: rkisp1: add Rockchip ISP1 base driver") Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 0f74153ce66b..b1b3c058e957 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -501,8 +501,7 @@ static int rkisp1_probe(struct platform_device *pdev) strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME, sizeof(rkisp1->media_dev.model)); rkisp1->media_dev.dev = &pdev->dev; - strscpy(rkisp1->media_dev.bus_info, - "platform: " RKISP1_DRIVER_NAME, + strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO, sizeof(rkisp1->media_dev.bus_info)); media_device_init(&rkisp1->media_dev); From c93015e0b76777f769e521775f61d68d553b9f1a Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Thu, 6 Feb 2020 23:07:09 +0100 Subject: [PATCH 0619/2231] media: staging: rkisp1: stats: use consistent bus_info string Stats entity is using a slightly different bus_info string "platform: rkisp1" (with a space) instead of "platform:rkisp1" used by the rest of rkisp1 code. This causes errors when using v4l2-util tools that uses the bus_info string to identify the device. Fixes: 9a28dbd65a88 ("media: staging: rkisp1: add capture device for statistics") Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-stats.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index c0ccc86b1275..6dfcbdc3deb8 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -70,8 +70,7 @@ static int rkisp1_stats_querycap(struct file *file, strscpy(cap->driver, RKISP1_DRIVER_NAME, sizeof(cap->driver)); strscpy(cap->card, vdev->name, sizeof(cap->card)); - strscpy(cap->bus_info, "platform: " RKISP1_DRIVER_NAME, - sizeof(cap->bus_info)); + strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info)); return 0; } From f8fb5729a99c14424f8ab19cf9d8a15ad2f30445 Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Thu, 6 Feb 2020 23:07:10 +0100 Subject: [PATCH 0620/2231] media: staging: rkisp1: rsz: don't ignore set format in bayer mainpath Fix issue when, in case of raw bayer format in mainpath, VIDIOC_SUBDEV_S_FMT ioctl didn't have any effect, is wasn't updating the format of the pad. There is no crop for mainpath bayer raw data. Remove leftover check in set_format, since check is already done in crop rkisp1_rsz_set_sink_crop() function. Fixes: d65dd85281fb ("media: staging: rkisp1: add Rockchip ISP1 base driver") Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-resizer.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 8cdc29c1a178..d986953d268b 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -537,15 +537,6 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, if (which == V4L2_SUBDEV_FORMAT_ACTIVE) rsz->fmt_type = mbus_info->fmt_type; - if (rsz->id == RKISP1_MAINPATH && - mbus_info->fmt_type == RKISP1_FMT_BAYER) { - sink_crop->left = 0; - sink_crop->top = 0; - sink_crop->width = sink_fmt->width; - sink_crop->height = sink_fmt->height; - return; - } - /* Propagete to source pad */ src_fmt->code = sink_fmt->code; From 20e46fc1029f195b40f1f437e5de47f553005373 Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Thu, 6 Feb 2020 23:07:11 +0100 Subject: [PATCH 0621/2231] media: staging: rkisp1: rsz: return to userspace the crop value in bayer mainpath If bayer format is set in mainpath, then crop is not supported. The core was just resetting crop but wasn't returning the values back to user space. Return values taken in consideration by the driver to userpace. Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-resizer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index d986953d268b..9de6744f0900 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -503,6 +503,8 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, sink_crop->top = 0; sink_crop->width = sink_fmt->width; sink_crop->height = sink_fmt->height; + + *r = *sink_crop; return; } From 100f720aabab3d5f58f67c508186041b3c797a9b Mon Sep 17 00:00:00 2001 From: Helen Koike Date: Thu, 6 Feb 2020 23:07:12 +0100 Subject: [PATCH 0622/2231] media: staging: rkisp1: isp: do not set invalid mbus code for pad When setting source pad, check if the given mbus code is indeed valid for source pad, if not, then set the default code. Same for sink pad. Fixes: d65dd85281fb ("media: staging: rkisp1: add Rockchip ISP1 base driver") Reported-by: Wojciech Zabolotny Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-isp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 7b1a5909a59e..c820a43634ac 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -683,7 +683,7 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, src_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SRC)) { src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); } @@ -767,7 +767,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } From 0966f4e5a8a2acc9c1b7aaaa4e8b35bf947a53cd Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 7 Feb 2020 09:59:48 +0100 Subject: [PATCH 0623/2231] media: staging: rkisp1: change function to return void instead of int There are functions that return int but actually return always 0. Change them to return void and then there is no need to check for error return value. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-isp.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index c820a43634ac..5ff4475032ae 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -504,7 +504,7 @@ static int rkisp1_config_cif(struct rkisp1_device *rkisp1) return 0; } -static int rkisp1_isp_stop(struct rkisp1_device *rkisp1) +static void rkisp1_isp_stop(struct rkisp1_device *rkisp1) { u32 val; @@ -540,8 +540,6 @@ static int rkisp1_isp_stop(struct rkisp1_device *rkisp1) RKISP1_CIF_IRCL_MIPI_SW_RST | RKISP1_CIF_IRCL_ISP_SW_RST, RKISP1_CIF_IRCL); rkisp1_write(rkisp1, 0x0, RKISP1_CIF_IRCL); - - return 0; } static void rkisp1_config_clk(struct rkisp1_device *rkisp1) @@ -555,7 +553,7 @@ static void rkisp1_config_clk(struct rkisp1_device *rkisp1) rkisp1_write(rkisp1, val, RKISP1_CIF_ICCL); } -static int rkisp1_isp_start(struct rkisp1_device *rkisp1) +static void rkisp1_isp_start(struct rkisp1_device *rkisp1) { struct rkisp1_sensor_async *sensor = rkisp1->active_sensor; u32 val; @@ -580,8 +578,6 @@ static int rkisp1_isp_start(struct rkisp1_device *rkisp1) * the MIPI interface and before starting the sensor output. */ usleep_range(1000, 1200); - - return 0; } /* ---------------------------------------------------------------------------- @@ -940,9 +936,7 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) int ret = 0; if (!enable) { - ret = rkisp1_isp_stop(rkisp1); - if (ret) - return ret; + rkisp1_isp_stop(rkisp1); rkisp1_mipi_csi2_stop(rkisp1->active_sensor); return 0; } @@ -965,9 +959,7 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) if (ret) return ret; - ret = rkisp1_isp_start(rkisp1); - if (ret) - rkisp1_mipi_csi2_stop(rkisp1->active_sensor); + rkisp1_isp_start(rkisp1); return ret; } From 129f757cd0ec3268820e20687009c97915fe1182 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 7 Feb 2020 09:59:49 +0100 Subject: [PATCH 0624/2231] media: staging: rkisp1: isp: check for dphy bus before initializations in s_stream In rkisp1_isp_s_stream it is better to return error in case the bus type is not dphy before initializing the registers. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-isp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 5ff4475032ae..9ad02bba14c0 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -947,14 +947,14 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) rkisp1->active_sensor = container_of(sensor_sd->asd, struct rkisp1_sensor_async, asd); + if (rkisp1->active_sensor->mbus.type != V4L2_MBUS_CSI2_DPHY) + return -EINVAL; + atomic_set(&rkisp1->isp.frame_sequence, -1); ret = rkisp1_config_cif(rkisp1); if (ret) return ret; - if (rkisp1->active_sensor->mbus.type != V4L2_MBUS_CSI2_DPHY) - return -EINVAL; - ret = rkisp1_mipi_csi2_start(&rkisp1->isp, rkisp1->active_sensor); if (ret) return ret; From 99adeeb67cecf57c2cd1837f3eed48672a884d94 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 7 Feb 2020 09:59:50 +0100 Subject: [PATCH 0625/2231] media: staging: rkisp1: add serialization to the isp subdev ops For subdevices drivers, the drivers themself are responsible for serializing their operations. This patch adds serialization to the isp subdevice. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 2 ++ drivers/staging/media/rkisp1/rkisp1-isp.c | 29 ++++++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 369a401b098a..6903c013dfdf 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -96,6 +96,7 @@ struct rkisp1_sensor_async { * @sink_crop: crop for sink pad * @src_fmt: output format * @src_crop: output size + * @ops_lock: ops serialization * * @is_dphy_errctrl_disabled : if dphy errctrl is disabled (avoid endless interrupt) * @frame_sequence: used to synchronize frame_id between video devices. @@ -107,6 +108,7 @@ struct rkisp1_isp { struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; const struct rkisp1_isp_mbus_info *sink_fmt; const struct rkisp1_isp_mbus_info *src_fmt; + struct mutex ops_lock; bool is_dphy_errctrl_disabled; atomic_t frame_sequence; }; diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 9ad02bba14c0..fa53f05e37d8 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -791,7 +791,9 @@ static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd, { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + mutex_lock(&isp->ops_lock); fmt->format = *rkisp1_isp_get_pad_fmt(isp, cfg, fmt->pad, fmt->which); + mutex_unlock(&isp->ops_lock); return 0; } @@ -801,6 +803,7 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + mutex_lock(&isp->ops_lock); if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO) rkisp1_isp_set_sink_fmt(isp, cfg, &fmt->format, fmt->which); else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) @@ -809,6 +812,7 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, fmt->format = *rkisp1_isp_get_pad_fmt(isp, cfg, fmt->pad, fmt->which); + mutex_unlock(&isp->ops_lock); return 0; } @@ -817,11 +821,13 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + int ret = 0; if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO && sel->pad != RKISP1_ISP_PAD_SINK_VIDEO) return -EINVAL; + mutex_lock(&isp->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { @@ -844,10 +850,10 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, sel->which); break; default: - return -EINVAL; + ret = -EINVAL; } - - return 0; + mutex_unlock(&isp->ops_lock); + return ret; } static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, @@ -857,21 +863,23 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, struct rkisp1_device *rkisp1 = container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev); struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + int ret = 0; if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; dev_dbg(rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); - + mutex_lock(&isp->ops_lock); if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) rkisp1_isp_set_sink_crop(isp, cfg, &sel->r, sel->which); else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) rkisp1_isp_set_src_crop(isp, cfg, &sel->r, sel->which); else - return -EINVAL; + ret = -EINVAL; - return 0; + mutex_unlock(&isp->ops_lock); + return ret; } static int rkisp1_subdev_link_validate(struct media_link *link) @@ -932,6 +940,7 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) { struct rkisp1_device *rkisp1 = container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev); + struct rkisp1_isp *isp = &rkisp1->isp; struct v4l2_subdev *sensor_sd; int ret = 0; @@ -951,16 +960,19 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) return -EINVAL; atomic_set(&rkisp1->isp.frame_sequence, -1); + mutex_lock(&isp->ops_lock); ret = rkisp1_config_cif(rkisp1); if (ret) - return ret; + goto mutex_unlock; ret = rkisp1_mipi_csi2_start(&rkisp1->isp, rkisp1->active_sensor); if (ret) - return ret; + goto mutex_unlock; rkisp1_isp_start(rkisp1); +mutex_unlock: + mutex_unlock(&isp->ops_lock); return ret; } @@ -1020,6 +1032,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1, isp->sink_fmt = rkisp1_isp_mbus_info_get(RKISP1_DEF_SINK_PAD_FMT); isp->src_fmt = rkisp1_isp_mbus_info_get(RKISP1_DEF_SRC_PAD_FMT); + mutex_init(&isp->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads); if (ret) return ret; From f86a183cda8798899ffc33031f17afd788b1ec90 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Fri, 7 Feb 2020 09:59:51 +0100 Subject: [PATCH 0626/2231] media: staging: rkisp1: add serialization to the resizer subdev ops For subdevices drivers, the drivers themself are responsible for serializing their operations. This patch adds serialization to the resizer subdevice. Signed-off-by: Dafna Hirschfeld Acked-by: Helen Koike Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/rkisp1/rkisp1-common.h | 1 + drivers/staging/media/rkisp1/rkisp1-resizer.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 6903c013dfdf..b291cc60de8e 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -226,6 +226,7 @@ struct rkisp1_resizer { struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; const struct rkisp1_rsz_config *config; enum rkisp1_fmt_pix_type fmt_type; + struct mutex ops_lock; }; struct rkisp1_debug { diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 9de6744f0900..87799fbf0363 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -562,7 +562,9 @@ static int rkisp1_rsz_get_fmt(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); + mutex_lock(&rsz->ops_lock); fmt->format = *rkisp1_rsz_get_pad_fmt(rsz, cfg, fmt->pad, fmt->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -573,11 +575,13 @@ static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); + mutex_lock(&rsz->ops_lock); if (fmt->pad == RKISP1_RSZ_PAD_SINK) rkisp1_rsz_set_sink_fmt(rsz, cfg, &fmt->format, fmt->which); else rkisp1_rsz_set_src_fmt(rsz, cfg, &fmt->format, fmt->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -588,10 +592,12 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); struct v4l2_mbus_framefmt *mf_sink; + int ret = 0; if (sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; + mutex_lock(&rsz->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: mf_sink = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SINK, @@ -606,10 +612,11 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, sel->which); break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; + mutex_unlock(&rsz->ops_lock); + return ret; } static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, @@ -625,7 +632,9 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, dev_dbg(sd->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); + mutex_lock(&rsz->ops_lock); rkisp1_rsz_set_sink_crop(rsz, cfg, &sel->r, sel->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -665,9 +674,11 @@ static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable) if (other->is_streaming) when = RKISP1_SHADOW_REGS_ASYNC; + mutex_lock(&rsz->ops_lock); rkisp1_rsz_config(rsz, when); rkisp1_dcrop_config(rsz); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -713,6 +724,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) rsz->fmt_type = RKISP1_DEF_FMT_TYPE; + mutex_init(&rsz->ops_lock); ret = media_entity_pads_init(&sd->entity, 2, pads); if (ret) return ret; From 218fc9f2fcfbad70e7b2111eca591b1dc8e6c8a9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 10 Feb 2020 11:42:38 +0100 Subject: [PATCH 0627/2231] media: Documentation/media/uapi: more readable unions Avoid adding an extra columns when describing unions in the documentation. That makes it much harder to read. See e.g. VIDIOC_QUERY_EXT_CTRLS. Instead start off a union with 'union {' and end it with an extra row containing '}'. This leaves a lot more space for the description of the fields. This formatting technique was used in a few places already, but this patches fixes all remaining occurrences of 'union' in the media uAPI. Signed-off-by: Hans Verkuil Reported-by: Nicolas Dufresne Signed-off-by: Mauro Carvalho Chehab --- .../uapi/cec/cec-ioc-adap-g-conn-info.rst | 10 ++-- .../media/uapi/cec/cec-ioc-dqevent.rst | 20 +++---- .../uapi/mediactl/media-ioc-enum-entities.rst | 24 +++----- Documentation/media/uapi/v4l/buffer.rst | 53 ++++++------------ .../media/uapi/v4l/dev-sliced-vbi.rst | 15 ++--- .../media/uapi/v4l/pixfmt-v4l2-mplane.rst | 6 +- Documentation/media/uapi/v4l/pixfmt-v4l2.rst | 2 - .../media/uapi/v4l/vidioc-dbg-g-chip-info.rst | 12 ++-- .../media/uapi/v4l/vidioc-dbg-g-register.rst | 12 ++-- .../media/uapi/v4l/vidioc-decoder-cmd.rst | 26 +++------ .../media/uapi/v4l/vidioc-dqevent.rst | 55 ++++++------------- .../media/uapi/v4l/vidioc-dv-timings-cap.rst | 14 ++--- .../uapi/v4l/vidioc-enum-frameintervals.rst | 19 +++---- .../media/uapi/v4l/vidioc-enum-framesizes.rst | 18 +++--- .../media/uapi/v4l/vidioc-g-dv-timings.rst | 16 +++--- .../media/uapi/v4l/vidioc-g-ext-ctrls.rst | 45 ++++++--------- Documentation/media/uapi/v4l/vidioc-g-fmt.rst | 29 ++++------ .../media/uapi/v4l/vidioc-g-parm.rst | 18 +++--- .../media/uapi/v4l/vidioc-queryctrl.rst | 19 +++---- 19 files changed, 153 insertions(+), 260 deletions(-) diff --git a/Documentation/media/uapi/cec/cec-ioc-adap-g-conn-info.rst b/Documentation/media/uapi/cec/cec-ioc-adap-g-conn-info.rst index a21659d55c6b..6818ddf1495c 100644 --- a/Documentation/media/uapi/cec/cec-ioc-adap-g-conn-info.rst +++ b/Documentation/media/uapi/cec/cec-ioc-adap-g-conn-info.rst @@ -44,18 +44,18 @@ is only available if the ``CEC_CAP_CONNECTOR_INFO`` capability is set. .. flat-table:: struct cec_connector_info :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 8 + :widths: 1 1 8 * - __u32 - ``type`` - The type of connector this adapter is associated with. - * - union + * - union { - ``(anonymous)`` - - - * - - - ``struct cec_drm_connector_info`` + * - ``struct cec_drm_connector_info`` - drm - :ref:`cec-drm-connector-info` + * - } + - .. tabularcolumns:: |p{4.4cm}|p{2.5cm}|p{10.6cm}| diff --git a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst index 5e21b1fbfc01..d16b226b1bef 100644 --- a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst +++ b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst @@ -109,35 +109,33 @@ it is guaranteed that the state did change in between the two events. .. flat-table:: struct cec_event :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 8 + :widths: 1 1 8 * - __u64 - ``ts`` - - :cspan:`1`\ Timestamp of the event in ns. + - Timestamp of the event in ns. The timestamp has been taken from the ``CLOCK_MONOTONIC`` clock. To access the same clock from userspace use :c:func:`clock_gettime`. * - __u32 - ``event`` - - :cspan:`1` The CEC event type, see :ref:`cec-events`. + - The CEC event type, see :ref:`cec-events`. * - __u32 - ``flags`` - - :cspan:`1` Event flags, see :ref:`cec-event-flags`. - * - union + - Event flags, see :ref:`cec-event-flags`. + * - union { - (anonymous) - - - - - * - - - struct cec_event_state_change + * - struct cec_event_state_change - ``state_change`` - The new adapter state as sent by the :ref:`CEC_EVENT_STATE_CHANGE ` event. - * - - - struct cec_event_lost_msgs + * - struct cec_event_lost_msgs - ``lost_msgs`` - The number of lost messages as sent by the :ref:`CEC_EVENT_LOST_MSGS ` event. + * - } + - .. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{11.0cm}| diff --git a/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst b/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst index 6218d9cbdd83..33e2b110145c 100644 --- a/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst +++ b/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst @@ -64,12 +64,11 @@ id's until they get an error. .. flat-table:: struct media_entity_desc :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 1 8 + :widths: 2 2 1 8 * - __u32 - ``id`` - - - - Entity ID, set by the application. When the ID is or'ed with ``MEDIA_ENT_ID_FLAG_NEXT``, the driver clears the flag and returns the first entity with a larger ID. Do not expect that the ID will @@ -79,79 +78,70 @@ id's until they get an error. * - char - ``name``\ [32] - - - - Entity name as an UTF-8 NULL-terminated string. This name must be unique within the media topology. * - __u32 - ``type`` - - - - Entity type, see :ref:`media-entity-functions` for details. * - __u32 - ``revision`` - - - - Entity revision. Always zero (obsolete) * - __u32 - ``flags`` - - - - Entity flags, see :ref:`media-entity-flag` for details. * - __u32 - ``group_id`` - - - - Entity group ID. Always zero (obsolete) * - __u16 - ``pads`` - - - - Number of pads * - __u16 - ``links`` - - - - Total number of outbound links. Inbound links are not counted in this field. * - __u32 - ``reserved[4]`` - - - - Reserved for future extensions. Drivers and applications must set the array to zero. - * - union + * - union { + - (anonymous) - * - - - struct + * - struct - ``dev`` - - Valid for (sub-)devices that create a single device node. * - - - - __u32 - ``major`` - Device node major number. * - - - - __u32 - ``minor`` - Device node minor number. - * - - - __u8 + * - __u8 - ``raw``\ [184] - - + * - } + - Return Value diff --git a/Documentation/media/uapi/v4l/buffer.rst b/Documentation/media/uapi/v4l/buffer.rst index 9149b57728e5..3112300c2fa0 100644 --- a/Documentation/media/uapi/v4l/buffer.rst +++ b/Documentation/media/uapi/v4l/buffer.rst @@ -172,11 +172,10 @@ struct v4l2_buffer .. flat-table:: struct v4l2_buffer :header-rows: 0 :stub-columns: 0 - :widths: 1 2 1 10 + :widths: 1 2 10 * - __u32 - ``index`` - - - Number of the buffer, set by the application except when calling :ref:`VIDIOC_DQBUF `, then it is set by the driver. This field can range from zero to the number of buffers @@ -186,14 +185,12 @@ struct v4l2_buffer :ref:`VIDIOC_CREATE_BUFS` minus one. * - __u32 - ``type`` - - - Type of the buffer, same as struct :c:type:`v4l2_format` ``type`` or struct :c:type:`v4l2_requestbuffers` ``type``, set by the application. See :c:type:`v4l2_buf_type` * - __u32 - ``bytesused`` - - - The number of bytes occupied by the data in the buffer. It depends on the negotiated data format and may change with each buffer for compressed variable size data like JPEG images. Drivers must set @@ -205,18 +202,15 @@ struct v4l2_buffer ``planes`` pointer is used instead. * - __u32 - ``flags`` - - - Flags set by the application or driver, see :ref:`buffer-flags`. * - __u32 - ``field`` - - - Indicates the field order of the image in the buffer, see :c:type:`v4l2_field`. This field is not used when the buffer contains VBI data. Drivers must set it when ``type`` refers to a capture stream, applications when it refers to an output stream. * - struct timeval - ``timestamp`` - - - For capture streams this is time when the first data byte was captured, as returned by the :c:func:`clock_gettime()` function for the relevant clock id; see ``V4L2_BUF_FLAG_TIMESTAMP_*`` in @@ -229,7 +223,6 @@ struct v4l2_buffer stream. * - struct :c:type:`v4l2_timecode` - ``timecode`` - - - When the ``V4L2_BUF_FLAG_TIMECODE`` flag is set in ``flags``, this structure contains a frame timecode. In :c:type:`V4L2_FIELD_ALTERNATE ` mode the top and @@ -239,10 +232,9 @@ struct v4l2_buffer independent of the ``timestamp`` and ``sequence`` fields. * - __u32 - ``sequence`` - - - Set by the driver, counting the frames (not fields!) in sequence. This field is set for both input and output devices. - * - :cspan:`3` + * - :cspan:`2` In :c:type:`V4L2_FIELD_ALTERNATE ` mode the top and bottom field have the same sequence number. The count starts at @@ -262,13 +254,11 @@ struct v4l2_buffer * - __u32 - ``memory`` - - - This field must be set by applications and/or drivers in accordance with the selected I/O method. See :c:type:`v4l2_memory` - * - union + * - union { - ``m`` - * - - - __u32 + * - __u32 - ``offset`` - For the single-planar API and when ``memory`` is ``V4L2_MEMORY_MMAP`` this is the offset of the buffer from the @@ -276,29 +266,27 @@ struct v4l2_buffer and apart of serving as parameter to the :ref:`mmap() ` function not useful for applications. See :ref:`mmap` for details - * - - - unsigned long + * - unsigned long - ``userptr`` - For the single-planar API and when ``memory`` is ``V4L2_MEMORY_USERPTR`` this is a pointer to the buffer (casted to unsigned long type) in virtual memory, set by the application. See :ref:`userp` for details. - * - - - struct v4l2_plane + * - struct v4l2_plane - ``*planes`` - When using the multi-planar API, contains a userspace pointer to an array of struct :c:type:`v4l2_plane`. The size of the array should be put in the ``length`` field of this struct :c:type:`v4l2_buffer` structure. - * - - - int + * - int - ``fd`` - For the single-plane API and when ``memory`` is ``V4L2_MEMORY_DMABUF`` this is the file descriptor associated with a DMABUF buffer. + * - } + - * - __u32 - ``length`` - - - Size of the buffer (not the payload) in bytes for the single-planar API. This is set by the driver based on the calls to :ref:`VIDIOC_REQBUFS` and/or @@ -308,12 +296,10 @@ struct v4l2_buffer actual number of valid elements in that array. * - __u32 - ``reserved2`` - - - A place holder for future extensions. Drivers and applications must set this to 0. * - __u32 - ``request_fd`` - - - The file descriptor of the request to queue the buffer to. If the flag ``V4L2_BUF_FLAG_REQUEST_FD`` is set, then the buffer will be queued to this request. If the flag is not set, then this field will @@ -344,11 +330,10 @@ struct v4l2_plane .. flat-table:: :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``bytesused`` - - - The number of bytes occupied by data in the plane (its payload). Drivers must set this field when ``type`` refers to a capture stream, applications when it refers to an output stream. If the @@ -362,40 +347,35 @@ struct v4l2_plane which may not be 0. * - __u32 - ``length`` - - - Size in bytes of the plane (not its payload). This is set by the driver based on the calls to :ref:`VIDIOC_REQBUFS` and/or :ref:`VIDIOC_CREATE_BUFS`. - * - union + * - union { - ``m`` - - - - - * - - - __u32 + * - __u32 - ``mem_offset`` - When the memory type in the containing struct :c:type:`v4l2_buffer` is ``V4L2_MEMORY_MMAP``, this is the value that should be passed to :ref:`mmap() `, similar to the ``offset`` field in struct :c:type:`v4l2_buffer`. - * - - - unsigned long + * - unsigned long - ``userptr`` - When the memory type in the containing struct :c:type:`v4l2_buffer` is ``V4L2_MEMORY_USERPTR``, this is a userspace pointer to the memory allocated for this plane by an application. - * - - - int + * - int - ``fd`` - When the memory type in the containing struct :c:type:`v4l2_buffer` is ``V4L2_MEMORY_DMABUF``, this is a file descriptor associated with a DMABUF buffer, similar to the ``fd`` field in struct :c:type:`v4l2_buffer`. + * - } + - * - __u32 - ``data_offset`` - - - Offset in bytes to video data in the plane. Drivers must set this field when ``type`` refers to a capture stream, applications when it refers to an output stream. @@ -407,7 +387,6 @@ struct v4l2_plane at offset ``data_offset`` from the start of the plane. * - __u32 - ``reserved[11]`` - - - Reserved for future use. Should be zeroed by drivers and applications. diff --git a/Documentation/media/uapi/v4l/dev-sliced-vbi.rst b/Documentation/media/uapi/v4l/dev-sliced-vbi.rst index e86346f66017..7b2d38dd402a 100644 --- a/Documentation/media/uapi/v4l/dev-sliced-vbi.rst +++ b/Documentation/media/uapi/v4l/dev-sliced-vbi.rst @@ -478,33 +478,30 @@ struct v4l2_mpeg_vbi_fmt_ivtv .. flat-table:: :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u8 - ``magic``\ [4] - - - A "magic" constant from :ref:`v4l2-mpeg-vbi-fmt-ivtv-magic` that indicates this is a valid sliced VBI data payload and also indicates which member of the anonymous union, ``itv0`` or ``ITV0``, to use for the payload data. - * - union + * - union { - (anonymous) - * - - - struct :c:type:`v4l2_mpeg_vbi_itv0` + * - struct :c:type:`v4l2_mpeg_vbi_itv0` - ``itv0`` - The primary form of the sliced VBI data payload that contains anywhere from 1 to 35 lines of sliced VBI data. Line masks are provided in this form of the payload indicating which VBI lines are provided. - * - - - struct :ref:`v4l2_mpeg_vbi_ITV0 ` + * - struct :ref:`v4l2_mpeg_vbi_ITV0 ` - ``ITV0`` - An alternate form of the sliced VBI data payload used when 36 lines of sliced VBI data are present. No line masks are provided in this form of the payload; all valid line mask bits are implcitly set. - - + * - } + - .. _v4l2-mpeg-vbi-fmt-ivtv-magic: diff --git a/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst b/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst index db43dda5aafb..054275c0dfc1 100644 --- a/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst +++ b/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst @@ -100,7 +100,8 @@ describing all planes of that format. * - __u8 - ``flags`` - Flags set by the application or driver, see :ref:`format-flags`. - * - :cspan:`2` union { (anonymous) + * - union { + - (anonymous) * - __u8 - ``ycbcr_enc`` - Y'CbCr encoding, from enum :c:type:`v4l2_ycbcr_encoding`. @@ -113,7 +114,8 @@ describing all planes of that format. This information supplements the ``colorspace`` and must be set by the driver for capture streams and by the application for output streams, see :ref:`colorspaces`. - * - :cspan:`2` } + * - } + - * - __u8 - ``quantization`` - Quantization range, from enum :c:type:`v4l2_quantization`. diff --git a/Documentation/media/uapi/v4l/pixfmt-v4l2.rst b/Documentation/media/uapi/v4l/pixfmt-v4l2.rst index a8321c348bf8..a993b861bf75 100644 --- a/Documentation/media/uapi/v4l/pixfmt-v4l2.rst +++ b/Documentation/media/uapi/v4l/pixfmt-v4l2.rst @@ -143,7 +143,6 @@ Single-planar format structure - Flags set by the application or driver, see :ref:`format-flags`. * - union { - (anonymous) - - * - __u32 - ``ycbcr_enc`` - Y'CbCr encoding, from enum :c:type:`v4l2_ycbcr_encoding`. @@ -158,7 +157,6 @@ Single-planar format structure streams, see :ref:`colorspaces`. * - } - - - * - __u32 - ``quantization`` - Quantization range, from enum :c:type:`v4l2_quantization`. diff --git a/Documentation/media/uapi/v4l/vidioc-dbg-g-chip-info.rst b/Documentation/media/uapi/v4l/vidioc-dbg-g-chip-info.rst index a1cf20181cf1..d38031dbe4e4 100644 --- a/Documentation/media/uapi/v4l/vidioc-dbg-g-chip-info.rst +++ b/Documentation/media/uapi/v4l/vidioc-dbg-g-chip-info.rst @@ -91,23 +91,23 @@ instructions. .. flat-table:: struct v4l2_dbg_match :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``type`` - See :ref:`name-chip-match-types` for a list of possible types. - * - union + * - union { - (anonymous) - * - - - __u32 + * - __u32 - ``addr`` - Match a chip by this number, interpreted according to the ``type`` field. - * - - - char + * - char - ``name[32]`` - Match a chip by this name, interpreted according to the ``type`` field. Currently unused. + * - } + - diff --git a/Documentation/media/uapi/v4l/vidioc-dbg-g-register.rst b/Documentation/media/uapi/v4l/vidioc-dbg-g-register.rst index 29e1d4fc4f52..112597c6cad2 100644 --- a/Documentation/media/uapi/v4l/vidioc-dbg-g-register.rst +++ b/Documentation/media/uapi/v4l/vidioc-dbg-g-register.rst @@ -100,23 +100,23 @@ instructions. .. flat-table:: struct v4l2_dbg_match :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``type`` - See :ref:`chip-match-types` for a list of possible types. - * - union + * - union { - (anonymous) - * - - - __u32 + * - __u32 - ``addr`` - Match a chip by this number, interpreted according to the ``type`` field. - * - - - char + * - char - ``name[32]`` - Match a chip by this name, interpreted according to the ``type`` field. Currently unused. + * - } + - diff --git a/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst b/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst index f1a504836f31..784c5980da8d 100644 --- a/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst +++ b/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst @@ -77,32 +77,25 @@ introduced in Linux 3.3. They are, however, mandatory for stateful mem2mem decod .. flat-table:: struct v4l2_decoder_cmd :header-rows: 0 :stub-columns: 0 - :widths: 11 24 12 16 106 + :widths: 1 1 1 3 * - __u32 - ``cmd`` - - - - The decoder command, see :ref:`decoder-cmds`. * - __u32 - ``flags`` - - - - Flags to go with the command. If no flags are defined for this command, drivers and applications must set this field to zero. - * - union + * - union { - (anonymous) - - - - - - - * - - - struct + * - struct - ``start`` - - Structure containing additional data for the ``V4L2_DEC_CMD_START`` command. * - - - - __s32 - ``speed`` - Playback speed and direction. The playback speed is defined as @@ -113,7 +106,6 @@ introduced in Linux 3.3. They are, however, mandatory for stateful mem2mem decod of 1 steps just one frame forward, a speed of -1 steps just one frame back. * - - - - __u32 - ``format`` - Format restrictions. This field is set by the driver, not the @@ -124,30 +116,26 @@ introduced in Linux 3.3. They are, however, mandatory for stateful mem2mem decod GOPs, which it can then play in reverse order. So to implement reverse playback the application must feed the decoder the last GOP in the video file, then the GOP before that, etc. etc. - * - - - struct + * - struct - ``stop`` - - Structure containing additional data for the ``V4L2_DEC_CMD_STOP`` command. * - - - - __u64 - ``pts`` - Stop playback at this ``pts`` or immediately if the playback is already past that timestamp. Leave to 0 if you want to stop after the last frame was decoded. - * - - - struct + * - struct - ``raw`` - - - - * - - - - __u32 - ``data``\ [16] - Reserved for future extensions. Drivers and applications must set the array to zero. + * - } + - diff --git a/Documentation/media/uapi/v4l/vidioc-dqevent.rst b/Documentation/media/uapi/v4l/vidioc-dqevent.rst index 42659a3d1705..2f37d255352a 100644 --- a/Documentation/media/uapi/v4l/vidioc-dqevent.rst +++ b/Documentation/media/uapi/v4l/vidioc-dqevent.rst @@ -55,66 +55,54 @@ call. .. flat-table:: struct v4l2_event :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - ``type`` - - - Type of the event, see :ref:`event-type`. - * - union + * - union { - ``u`` - - - - - * - - - struct :c:type:`v4l2_event_vsync` + * - struct :c:type:`v4l2_event_vsync` - ``vsync`` - Event data for event ``V4L2_EVENT_VSYNC``. - * - - - struct :c:type:`v4l2_event_ctrl` + * - struct :c:type:`v4l2_event_ctrl` - ``ctrl`` - Event data for event ``V4L2_EVENT_CTRL``. - * - - - struct :c:type:`v4l2_event_frame_sync` + * - struct :c:type:`v4l2_event_frame_sync` - ``frame_sync`` - Event data for event ``V4L2_EVENT_FRAME_SYNC``. - * - - - struct :c:type:`v4l2_event_motion_det` + * - struct :c:type:`v4l2_event_motion_det` - ``motion_det`` - Event data for event V4L2_EVENT_MOTION_DET. - * - - - struct :c:type:`v4l2_event_src_change` + * - struct :c:type:`v4l2_event_src_change` - ``src_change`` - Event data for event V4L2_EVENT_SOURCE_CHANGE. - * - - - __u8 + * - __u8 - ``data``\ [64] - Event data. Defined by the event type. The union should be used to define easily accessible type for events. + * - } + - * - __u32 - ``pending`` - - - Number of pending events excluding this one. * - __u32 - ``sequence`` - - - Event sequence number. The sequence number is incremented for every subscribed event that takes place. If sequence numbers are not contiguous it means that events have been lost. * - struct timespec - ``timestamp`` - - - Event timestamp. The timestamp has been taken from the ``CLOCK_MONOTONIC`` clock. To access the same clock outside V4L2, use :c:func:`clock_gettime`. * - u32 - ``id`` - - - The ID associated with the event source. If the event does not have an associated ID (this depends on the event type), then this is 0. * - __u32 - ``reserved``\ [8] - - - Reserved for future extensions. Drivers must set the array to zero. @@ -233,54 +221,45 @@ call. .. flat-table:: struct v4l2_event_ctrl :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - ``changes`` - - - A bitmask that tells what has changed. See :ref:`ctrl-changes-flags`. * - __u32 - ``type`` - - - The type of the control. See enum :c:type:`v4l2_ctrl_type`. - * - union (anonymous) - - - - - - - * - - - __s32 + * - union { + - (anonymous) + * - __s32 - ``value`` - The 32-bit value of the control for 32-bit control types. This is 0 for string controls since the value of a string cannot be passed using :ref:`VIDIOC_DQEVENT`. - * - - - __s64 + * - __s64 - ``value64`` - The 64-bit value of the control for 64-bit control types. + * - } + - * - __u32 - ``flags`` - - - The control flags. See :ref:`control-flags`. * - __s32 - ``minimum`` - - - The minimum value of the control. See struct :ref:`v4l2_queryctrl `. * - __s32 - ``maximum`` - - - The maximum value of the control. See struct :ref:`v4l2_queryctrl `. * - __s32 - ``step`` - - - The step value of the control. See struct :ref:`v4l2_queryctrl `. * - __s32 - ``default_value`` - - - The default value value of the control. See struct :ref:`v4l2_queryctrl `. diff --git a/Documentation/media/uapi/v4l/vidioc-dv-timings-cap.rst b/Documentation/media/uapi/v4l/vidioc-dv-timings-cap.rst index e62d45d37072..1d0acbf14c4f 100644 --- a/Documentation/media/uapi/v4l/vidioc-dv-timings-cap.rst +++ b/Documentation/media/uapi/v4l/vidioc-dv-timings-cap.rst @@ -112,7 +112,7 @@ that doesn't support them will return an ``EINVAL`` error code. .. flat-table:: struct v4l2_dv_timings_cap :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - ``type`` @@ -127,16 +127,14 @@ that doesn't support them will return an ``EINVAL`` error code. - Reserved for future extensions. Drivers and applications must set the array to zero. - * - union - - - - - * - - - struct :c:type:`v4l2_bt_timings_cap` + * - union { + - (anonymous) + * - struct :c:type:`v4l2_bt_timings_cap` - ``bt`` - BT.656/1120 timings capabilities of the hardware. - * - - - __u32 + * - __u32 - ``raw_data``\ [32] + * - } - .. tabularcolumns:: |p{7.0cm}|p{10.5cm}| diff --git a/Documentation/media/uapi/v4l/vidioc-enum-frameintervals.rst b/Documentation/media/uapi/v4l/vidioc-enum-frameintervals.rst index 2c69f26b165d..563a67cddeca 100644 --- a/Documentation/media/uapi/v4l/vidioc-enum-frameintervals.rst +++ b/Documentation/media/uapi/v4l/vidioc-enum-frameintervals.rst @@ -138,36 +138,31 @@ application should zero out all members except for the *IN* fields. * - __u32 - ``index`` - - - IN: Index of the given frame interval in the enumeration. * - __u32 - ``pixel_format`` - - - IN: Pixel format for which the frame intervals are enumerated. * - __u32 - ``width`` - - - IN: Frame width for which the frame intervals are enumerated. * - __u32 - ``height`` - - - IN: Frame height for which the frame intervals are enumerated. * - __u32 - ``type`` - - - OUT: Frame interval type the device supports. - * - union - - - - + * - union { + - (anonymous) - OUT: Frame interval with the given index. - * - - - struct :c:type:`v4l2_fract` + * - struct :c:type:`v4l2_fract` - ``discrete`` - Frame interval [s]. - * - - - struct :c:type:`v4l2_frmival_stepwise` + * - struct :c:type:`v4l2_frmival_stepwise` - ``stepwise`` - + * - } + - + - * - __u32 - ``reserved[2]`` - diff --git a/Documentation/media/uapi/v4l/vidioc-enum-framesizes.rst b/Documentation/media/uapi/v4l/vidioc-enum-framesizes.rst index cf31f548826f..cd97546a7122 100644 --- a/Documentation/media/uapi/v4l/vidioc-enum-framesizes.rst +++ b/Documentation/media/uapi/v4l/vidioc-enum-framesizes.rst @@ -155,31 +155,27 @@ application should zero out all members except for the *IN* fields. * - __u32 - ``index`` - - - IN: Index of the given frame size in the enumeration. * - __u32 - ``pixel_format`` - - - IN: Pixel format for which the frame sizes are enumerated. * - __u32 - ``type`` - - - OUT: Frame size type the device supports. - * - union - - - - + * - union { + - (anonymous) - OUT: Frame size with the given index. - * - - - struct :c:type:`v4l2_frmsize_discrete` + * - struct :c:type:`v4l2_frmsize_discrete` - ``discrete`` - - * - - - struct :c:type:`v4l2_frmsize_stepwise` + * - struct :c:type:`v4l2_frmsize_stepwise` - ``stepwise`` - + * - } + - + - * - __u32 - ``reserved[2]`` - - - Reserved space for future use. Must be zeroed by drivers and applications. diff --git a/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst b/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst index 5c675cbac4cf..e36dd2622857 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst @@ -179,23 +179,21 @@ EBUSY .. flat-table:: struct v4l2_dv_timings :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - ``type`` - - - Type of DV timings as listed in :ref:`dv-timing-types`. - * - union - - - - - * - - - struct :c:type:`v4l2_bt_timings` + * - union { + - (anonymous) + * - struct :c:type:`v4l2_bt_timings` - ``bt`` - Timings defined by BT.656/1120 specifications - * - - - __u32 + * - __u32 - ``reserved``\ [32] - + * - } + - .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| diff --git a/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst b/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst index 271cac18afbb..cdb2a2a512d6 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst @@ -136,15 +136,13 @@ still cause this situation. .. flat-table:: struct v4l2_ext_control :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``id`` - - - Identifies the control, set by the application. * - __u32 - ``size`` - - - The total size in bytes of the payload of this control. This is normally 0, but for pointer controls this should be set to the size of the memory containing the payload, or that will receive @@ -161,55 +159,48 @@ still cause this situation. *length* of the string may well be much smaller. * - __u32 - ``reserved2``\ [1] - - - Reserved for future extensions. Drivers and applications must set the array to zero. - * - union + * - union { - (anonymous) - * - - - __s32 + * - __s32 - ``value`` - New value or current value. Valid if this control is not of type ``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is not set. - * - - - __s64 + * - __s64 - ``value64`` - New value or current value. Valid if this control is of type ``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is not set. - * - - - char * + * - char * - ``string`` - A pointer to a string. Valid if this control is of type ``V4L2_CTRL_TYPE_STRING``. - * - - - __u8 * + * - __u8 * - ``p_u8`` - A pointer to a matrix control of unsigned 8-bit values. Valid if this control is of type ``V4L2_CTRL_TYPE_U8``. - * - - - __u16 * + * - __u16 * - ``p_u16`` - A pointer to a matrix control of unsigned 16-bit values. Valid if this control is of type ``V4L2_CTRL_TYPE_U16``. - * - - - __u32 * + * - __u32 * - ``p_u32`` - A pointer to a matrix control of unsigned 32-bit values. Valid if this control is of type ``V4L2_CTRL_TYPE_U32``. - * - - - :c:type:`v4l2_area` * + * - :c:type:`v4l2_area` * - ``p_area`` - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is of type ``V4L2_CTRL_TYPE_AREA``. - * - - - void * + * - void * - ``ptr`` - A pointer to a compound type which can be an N-dimensional array and/or a compound type (the control's type is >= ``V4L2_CTRL_COMPOUND_TYPES``). Valid if ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set for this control. + * - } + - .. tabularcolumns:: |p{4.0cm}|p{2.2cm}|p{2.1cm}|p{8.2cm}| @@ -221,12 +212,11 @@ still cause this situation. .. flat-table:: struct v4l2_ext_controls :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 - * - union + * - union { - (anonymous) - * - - - __u32 + * - __u32 - ``ctrl_class`` - The control class to which all controls belong, see :ref:`ctrl-class`. Drivers that use a kernel framework for @@ -235,8 +225,7 @@ still cause this situation. support this can be tested by setting ``ctrl_class`` to 0 and calling :ref:`VIDIOC_TRY_EXT_CTRLS ` with a ``count`` of 0. If that succeeds, then the driver supports this feature. - * - - - __u32 + * - __u32 - ``which`` - Which value of the control to get/set/try. ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of the @@ -261,6 +250,8 @@ still cause this situation. by setting ctrl_class to ``V4L2_CTRL_WHICH_CUR_VAL`` and calling VIDIOC_TRY_EXT_CTRLS with a count of 0. If that fails, then the driver does not support ``V4L2_CTRL_WHICH_CUR_VAL``. + * - } + - * - __u32 - ``count`` - The number of controls in the controls array. May also be zero. diff --git a/Documentation/media/uapi/v4l/vidioc-g-fmt.rst b/Documentation/media/uapi/v4l/vidioc-g-fmt.rst index e35a9caff652..1e69bfc46e8d 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-fmt.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-fmt.rst @@ -103,51 +103,44 @@ The format as returned by :ref:`VIDIOC_TRY_FMT ` must be identical * - __u32 - ``type`` - - - Type of the data stream, see :c:type:`v4l2_buf_type`. - * - union + * - union { - ``fmt`` - * - - - struct :c:type:`v4l2_pix_format` + * - struct :c:type:`v4l2_pix_format` - ``pix`` - Definition of an image format, see :ref:`pixfmt`, used by video capture and output devices. - * - - - struct :c:type:`v4l2_pix_format_mplane` + * - struct :c:type:`v4l2_pix_format_mplane` - ``pix_mp`` - Definition of an image format, see :ref:`pixfmt`, used by video capture and output devices that support the :ref:`multi-planar version of the API `. - * - - - struct :c:type:`v4l2_window` + * - struct :c:type:`v4l2_window` - ``win`` - Definition of an overlaid image, see :ref:`overlay`, used by video overlay devices. - * - - - struct :c:type:`v4l2_vbi_format` + * - struct :c:type:`v4l2_vbi_format` - ``vbi`` - Raw VBI capture or output parameters. This is discussed in more detail in :ref:`raw-vbi`. Used by raw VBI capture and output devices. - * - - - struct :c:type:`v4l2_sliced_vbi_format` + * - struct :c:type:`v4l2_sliced_vbi_format` - ``sliced`` - Sliced VBI capture or output parameters. See :ref:`sliced` for details. Used by sliced VBI capture and output devices. - * - - - struct :c:type:`v4l2_sdr_format` + * - struct :c:type:`v4l2_sdr_format` - ``sdr`` - Definition of a data format, see :ref:`pixfmt`, used by SDR capture and output devices. - * - - - struct :c:type:`v4l2_meta_format` + * - struct :c:type:`v4l2_meta_format` - ``meta`` - Definition of a metadata format, see :ref:`meta-formats`, used by metadata capture devices. - * - - - __u8 + * - __u8 - ``raw_data``\ [200] - Place holder for future extensions. + * - } + - Return Value diff --git a/Documentation/media/uapi/v4l/vidioc-g-parm.rst b/Documentation/media/uapi/v4l/vidioc-g-parm.rst index d9d5d97848d3..044a459e073f 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-parm.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-parm.rst @@ -69,33 +69,29 @@ union holding separate parameters for input and output devices. .. flat-table:: struct v4l2_streamparm :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``type`` - - - The buffer (stream) type, same as struct :c:type:`v4l2_format` ``type``, set by the application. See :c:type:`v4l2_buf_type`. - * - union + * - union { - ``parm`` - - - - - * - - - struct :c:type:`v4l2_captureparm` + * - struct :c:type:`v4l2_captureparm` - ``capture`` - Parameters for capture devices, used when ``type`` is ``V4L2_BUF_TYPE_VIDEO_CAPTURE`` or ``V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE``. - * - - - struct :c:type:`v4l2_outputparm` + * - struct :c:type:`v4l2_outputparm` - ``output`` - Parameters for output devices, used when ``type`` is ``V4L2_BUF_TYPE_VIDEO_OUTPUT`` or ``V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE``. - * - - - __u8 + * - __u8 - ``raw_data``\ [200] - A place holder for future extensions. + * - } + - diff --git a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst index 22ff3c6abd9c..8971f4cfb16e 100644 --- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst +++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst @@ -290,34 +290,29 @@ See also the examples in :ref:`control`. .. flat-table:: struct v4l2_querymenu :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - - - ``id`` - Identifies the control, set by the application from the respective struct :ref:`v4l2_queryctrl ` ``id``. * - __u32 - - - ``index`` - Index of the menu item, starting at zero, set by the application. - * - union - - - - - - - * - - - __u8 + * - union { + - (anonymous) + * - __u8 - ``name``\ [32] - Name of the menu item, a NUL-terminated ASCII string. This information is intended for the user. This field is valid for ``V4L2_CTRL_TYPE_MENU`` type controls. - * - - - __s64 + * - __s64 - ``value`` - Value of the integer menu item. This field is valid for ``V4L2_CTRL_TYPE_INTEGER_MENU`` type controls. - * - __u32 + * - } - + * - __u32 - ``reserved`` - Reserved for future extensions. Drivers must set the array to zero. From ef0ed05dcef8a74178a8b480cce23a377b1de2b8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 11 Feb 2020 08:35:46 +0100 Subject: [PATCH 0628/2231] media: staging/imx: Missing assignment in imx_media_capture_device_register() There was supposed to be a "ret = " assignment here, otherwise the error handling on the next line won't work. Fixes: 64b5a49df486 ("[media] media: imx: Add Capture Device Interface") Signed-off-by: Dan Carpenter Reviewed-by: Steve Longerbeam Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/imx/imx-media-capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index fca8f3847717..0dd6a2fafd89 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -778,7 +778,7 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) /* setup default format */ fmt_src.pad = priv->src_sd_pad; fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; - v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt_src); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt_src); if (ret) { v4l2_err(sd, "failed to get src_sd format\n"); goto unreg; From 14af1df3b03309f6204661e12c40cdf71bc8f773 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 24 Feb 2020 17:45:55 +0200 Subject: [PATCH 0629/2231] spi: pxa2xx: Return error codes from pxa2xx_spi_init_pdata() For better understanding what's going on on error path, return distinguished error codes instead of NULL pointer. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200224154556.11627-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 4c7a71f0fb3e..934bfe2c8a90 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1548,18 +1548,18 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) else if (pcidev_id) type = (enum pxa_ssp_type)pcidev_id->driver_data; else - return NULL; + return ERR_PTR(-EINVAL); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) - return NULL; + return ERR_PTR(-ENOMEM); ssp = &pdata->ssp; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ssp->mmio_base)) - return NULL; + return ERR_CAST(ssp->mmio_base); ssp->phys_base = res->start; @@ -1573,11 +1573,11 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) ssp->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(ssp->clk)) - return NULL; + return ERR_CAST(ssp->clk); ssp->irq = platform_get_irq(pdev, 0); if (ssp->irq < 0) - return NULL; + return ERR_PTR(ssp->irq); ssp->type = type; ssp->dev = &pdev->dev; @@ -1634,9 +1634,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) platform_info = dev_get_platdata(dev); if (!platform_info) { platform_info = pxa2xx_spi_init_pdata(pdev); - if (!platform_info) { + if (IS_ERR(platform_info)) { dev_err(&pdev->dev, "missing platform data\n"); - return -ENODEV; + return PTR_ERR(platform_info); } } From 3d24b2a470466aa901ba8354468fc1899259fd84 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 24 Feb 2020 17:45:56 +0200 Subject: [PATCH 0630/2231] spi: pxa2xx: drv_data can't be NULL in ->remove() There is no need for drv_data check against NULL, since it won't happen. Remove useless check. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200224154556.11627-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 934bfe2c8a90..8e8e18023ded 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1884,11 +1884,7 @@ out_error_controller_alloc: static int pxa2xx_spi_remove(struct platform_device *pdev) { struct driver_data *drv_data = platform_get_drvdata(pdev); - struct ssp_device *ssp; - - if (!drv_data) - return 0; - ssp = drv_data->ssp; + struct ssp_device *ssp = drv_data->ssp; pm_runtime_get_sync(&pdev->dev); From 43e96ef8b70c50f6054f20b8c357ee5881592082 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 21 Feb 2020 11:48:43 +1100 Subject: [PATCH 0631/2231] docs/core-api: Add Fedora instructions for GCC plugins Add an example of how to install the necessary packages for GCC plugins on Fedora. Signed-off-by: Michael Ellerman Reviewed-by: Kees Cook Signed-off-by: Jonathan Corbet --- Documentation/core-api/gcc-plugins.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/core-api/gcc-plugins.rst b/Documentation/core-api/gcc-plugins.rst index 8502f24396fb..4b1c10f88e30 100644 --- a/Documentation/core-api/gcc-plugins.rst +++ b/Documentation/core-api/gcc-plugins.rst @@ -72,6 +72,10 @@ e.g., on Ubuntu for gcc-4.9:: apt-get install gcc-4.9-plugin-dev +Or on Fedora:: + + dnf install gcc-plugin-devel + Enable a GCC plugin based feature in the kernel config:: CONFIG_GCC_PLUGIN_CYC_COMPLEXITY = y From 290d5388993eb40b9d5632aefb864cf1012a2bcc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Feb 2020 10:00:01 +0100 Subject: [PATCH 0632/2231] scripts: documentation-file-ref-check: improve :doc: handling There are some issues at the script with regards to :doc: tags: - It doesn't escape files under Documentation/sphinx, leading to false positives; - It doesn't handle root URLs, like :doc:`/x86/boot`; - It doesn't output the file with a bad reference. Address those things, in order to remove false positives from the list of problems. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/documentation-file-ref-check | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/documentation-file-ref-check b/scripts/documentation-file-ref-check index 7784c54aa38b..997202a18ddb 100755 --- a/scripts/documentation-file-ref-check +++ b/scripts/documentation-file-ref-check @@ -51,7 +51,9 @@ open IN, "git grep ':doc:\`' Documentation/|" or die "Failed to run git grep"; while () { next if (!m,^([^:]+):.*\:doc\:\`([^\`]+)\`,); + next if (m,sphinx/,); + my $file = $1; my $d = $1; my $doc_ref = $2; @@ -60,7 +62,12 @@ while () { $d =~ s,(.*/).*,$1,; $f =~ s,.*\<([^\>]+)\>,$1,; - $f ="$d$f.rst"; + if ($f =~ m,^/,) { + $f = "$f.rst"; + $f =~ s,^/,Documentation/,; + } else { + $f = "$d$f.rst"; + } next if (grep -e, glob("$f")); @@ -69,7 +76,7 @@ while () { } $doc_fix++; - print STDERR "$f: :doc:`$doc_ref`\n"; + print STDERR "$file: :doc:`$doc_ref`\n"; } close IN; From a3aead706dac19ca504c31ed5d6b3e141addbaec Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Feb 2020 10:00:07 +0100 Subject: [PATCH 0633/2231] docs: gpu: i915.rst: fix warnings due to file renames Fix two warnings due to file rename: WARNING: kernel-doc './scripts/kernel-doc -rst -enable-lineno -function csr support for dmc ./drivers/gpu/drm/i915/intel_csr.c' failed with return code 1 WARNING: kernel-doc './scripts/kernel-doc -rst -enable-lineno -internal ./drivers/gpu/drm/i915/intel_csr.c' failed with return code 2 Fixes: 06d3ff6e7451 ("drm/i915: move intel_csr.[ch] under display/") Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- Documentation/gpu/i915.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/gpu/i915.rst b/Documentation/gpu/i915.rst index e539c42a3e78..cc74e24ca3b5 100644 --- a/Documentation/gpu/i915.rst +++ b/Documentation/gpu/i915.rst @@ -207,10 +207,10 @@ DPIO CSR firmware support for DMC ---------------------------- -.. kernel-doc:: drivers/gpu/drm/i915/intel_csr.c +.. kernel-doc:: drivers/gpu/drm/i915/display/intel_csr.c :doc: csr support for dmc -.. kernel-doc:: drivers/gpu/drm/i915/intel_csr.c +.. kernel-doc:: drivers/gpu/drm/i915/display/intel_csr.c :internal: Video BIOS Table (VBT) From 2bd49cb581ed5a5fbd43811b952fe9552b737408 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Fri, 21 Feb 2020 17:55:02 +0100 Subject: [PATCH 0634/2231] docs: sysctl/kernel: document acpi_video_flags Based on the implementation in arch/x86/kernel/acpi/sleep.c, in particular the acpi_sleep_setup() function. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 6c0d8c55101c..6586e0e0c11f 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -51,8 +51,15 @@ free space valid for 30 seconds. acpi_video_flags ================ -See Documentation/kernel/power/video.txt, it allows mode of video boot -to be set during run time. +See :doc:`/power/video`. This allows the video resume mode to be set, +in a similar fashion to the ``acpi_sleep`` kernel parameter, by +combining the following values: + += ======= +1 s3_bios +2 s3_mode +4 s3_beep += ======= auto_msgmni From bf347b9da9bbba14b4af845b00d443f24d17d46d Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Wed, 19 Feb 2020 12:21:33 -0700 Subject: [PATCH 0635/2231] Documentation: fix a typo for intel_iommu=nobounce "untrusted" was mis-spelled as "unstrusted" Signed-off-by: Alex Hung Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/kernel-parameters.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 47cd55e339a5..56bf9b2a9ddf 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1775,7 +1775,7 @@ provided by tboot because it makes the system vulnerable to DMA attacks. nobounce [Default off] - Disable bounce buffer for unstrusted devices such as + Disable bounce buffer for untrusted devices such as the Thunderbolt devices. This will treat the untrusted devices as the trusted ones, hence might expose security risks of DMA attacks. From 021622df556b7213cffec1c0713f093fc7d045e3 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Wed, 19 Feb 2020 16:34:42 +0100 Subject: [PATCH 0636/2231] docs: add a script to check sysctl docs This script allows sysctl documentation to be checked against the kernel source code, to identify missing or obsolete entries. Running it against 5.5 shows for example that sysctl/kernel.rst has two obsolete entries and is missing 52 entries. Signed-off-by: Stephen Kitt Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/sysctl/kernel.rst | 3 + scripts/check-sysctl-docs | 181 ++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100755 scripts/check-sysctl-docs diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 6586e0e0c11f..1c48ab4bfe30 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -2,6 +2,9 @@ Documentation for /proc/sys/kernel/ =================================== +.. See scripts/check-sysctl-docs to keep this up to date + + Copyright (c) 1998, 1999, Rik van Riel Copyright (c) 2009, Shen Feng diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs new file mode 100755 index 000000000000..8bcb9e26c7bc --- /dev/null +++ b/scripts/check-sysctl-docs @@ -0,0 +1,181 @@ +#!/usr/bin/gawk -f +# SPDX-License-Identifier: GPL-2.0 + +# Script to check sysctl documentation against source files +# +# Copyright (c) 2020 Stephen Kitt + +# Example invocation: +# scripts/check-sysctl-docs -vtable="kernel" \ +# Documentation/admin-guide/sysctl/kernel.rst \ +# $(git grep -l register_sysctl_) +# +# Specify -vdebug=1 to see debugging information + +BEGIN { + if (!table) { + print "Please specify the table to look for using the table variable" > "/dev/stderr" + exit 1 + } +} + +# The following globals are used: +# children: maps ctl_table names and procnames to child ctl_table names +# documented: maps documented entries (each key is an entry) +# entries: maps ctl_table names and procnames to counts (so +# enumerating the subkeys for a given ctl_table lists its +# procnames) +# files: maps procnames to source file names +# paths: maps ctl_path names to paths +# curpath: the name of the current ctl_path struct +# curtable: the name of the current ctl_table struct +# curentry: the name of the current proc entry (procname when parsing +# a ctl_table, constructed path when parsing a ctl_path) + + +# Remove punctuation from the given value +function trimpunct(value) { + while (value ~ /^["&]/) { + value = substr(value, 2) + } + while (value ~ /[]["&,}]$/) { + value = substr(value, 1, length(value) - 1) + } + return value +} + +# Print the information for the given entry +function printentry(entry) { + seen[entry]++ + printf "* %s from %s", entry, file[entry] + if (documented[entry]) { + printf " (documented)" + } + print "" +} + + +# Stage 1: build the list of documented entries +FNR == NR && /^=+$/ { + if (prevline ~ /Documentation for/) { + # This is the main title + next + } + + # The previous line is a section title, parse it + $0 = prevline + if (debug) print "Parsing " $0 + inbrackets = 0 + for (i = 1; i <= NF; i++) { + if (length($i) == 0) { + continue + } + if (!inbrackets && substr($i, 1, 1) == "(") { + inbrackets = 1 + } + if (!inbrackets) { + token = trimpunct($i) + if (length(token) > 0 && token != "and") { + if (debug) print trimpunct($i) + documented[trimpunct($i)]++ + } + } + if (inbrackets && substr($i, length($i), 1) == ")") { + inbrackets = 0 + } + } +} + +FNR == NR { + prevline = $0 + next +} + + +# Stage 2: process each file and find all sysctl tables +BEGINFILE { + delete children + delete entries + delete paths + curpath = "" + curtable = "" + curentry = "" + if (debug) print "Processing file " FILENAME +} + +/^static struct ctl_path/ { + match($0, /static struct ctl_path ([^][]+)/, tables) + curpath = tables[1] + if (debug) print "Processing path " curpath +} + +/^static struct ctl_table/ { + match($0, /static struct ctl_table ([^][]+)/, tables) + curtable = tables[1] + if (debug) print "Processing table " curtable +} + +/^};$/ { + curpath = "" + curtable = "" + curentry = "" +} + +curpath && /\.procname[\t ]*=[\t ]*".+"/ { + match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) + if (curentry) { + curentry = curentry "/" names[1] + } else { + curentry = names[1] + } + if (debug) print "Setting path " curpath " to " curentry + paths[curpath] = curentry +} + +curtable && /\.procname[\t ]*=[\t ]*".+"/ { + match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) + curentry = names[1] + if (debug) print "Adding entry " curentry " to table " curtable + entries[curtable][curentry]++ + file[curentry] = FILENAME +} + +/\.child[\t ]*=/ { + child = trimpunct($NF) + if (debug) print "Linking child " child " to table " curtable " entry " curentry + children[curtable][curentry] = child +} + +/register_sysctl_table\(.*\)/ { + match($0, /register_sysctl_table\(([^)]+)\)/, tables) + if (debug) print "Registering table " tables[1] + if (children[tables[1]][table]) { + for (entry in entries[children[tables[1]][table]]) { + printentry(entry) + } + } +} + +/register_sysctl_paths\(.*\)/ { + match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables) + if (debug) print "Attaching table " tables[2] " to path " tables[1] + if (paths[tables[1]] == table) { + for (entry in entries[tables[2]]) { + printentry(entry) + } + } + split(paths[tables[1]], components, "/") + if (length(components) > 1 && components[1] == table) { + # Count the first subdirectory as seen + seen[components[2]]++ + } +} + + +END { + for (entry in documented) { + if (!seen[entry]) { + print "No implementation for " entry + } + } +} From ef45e78fdc11ac1794940c2ff4a6bf3bc4c45372 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 13 Feb 2020 18:23:11 +0530 Subject: [PATCH 0637/2231] docs: kref: Clarify the use of two kref_put() in example code Eventhough the current documentation explains that the reference count gets incremented by both kref_init() and kref_get(), it is often misunderstood that only one instance of kref_put() is needed in the example code. So let's clarify that a bit. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Jonathan Corbet --- Documentation/kref.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/kref.txt b/Documentation/kref.txt index 3af384156d7e..c61eea6f1bf2 100644 --- a/Documentation/kref.txt +++ b/Documentation/kref.txt @@ -128,6 +128,10 @@ since we already have a valid pointer that we own a refcount for. The put needs no lock because nothing tries to get the data without already holding a pointer. +In the above example, kref_put() will be called 2 times in both success +and error paths. This is necessary because the reference count got +incremented 2 times by kref_init() and kref_get(). + Note that the "before" in rule 1 is very important. You should never do something like:: From 1afd37c6a335369a7927c732a93c0558b64e40bb Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 30 Jan 2020 13:47:47 +0800 Subject: [PATCH 0638/2231] USB: serial: f81232: extract LSR handler Extract LSR handler to function that can be re-used by F81532A/534A/535/536. Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Johan Hovold --- drivers/usb/serial/f81232.c | 53 +++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 43fa1f0716b7..c07d376c743d 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -322,10 +322,38 @@ exit: __func__, retval); } +static char f81232_handle_lsr(struct usb_serial_port *port, u8 lsr) +{ + struct f81232_private *priv = usb_get_serial_port_data(port); + char tty_flag = TTY_NORMAL; + + if (!(lsr & UART_LSR_BRK_ERROR_BITS)) + return tty_flag; + + if (lsr & UART_LSR_BI) { + tty_flag = TTY_BREAK; + port->icount.brk++; + usb_serial_handle_break(port); + } else if (lsr & UART_LSR_PE) { + tty_flag = TTY_PARITY; + port->icount.parity++; + } else if (lsr & UART_LSR_FE) { + tty_flag = TTY_FRAME; + port->icount.frame++; + } + + if (lsr & UART_LSR_OE) { + port->icount.overrun++; + schedule_work(&priv->lsr_work); + tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); + } + + return tty_flag; +} + static void f81232_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct f81232_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; char tty_flag; unsigned int i; @@ -341,29 +369,8 @@ static void f81232_process_read_urb(struct urb *urb) /* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */ for (i = 0; i < urb->actual_length; i += 2) { - tty_flag = TTY_NORMAL; lsr = data[i]; - - if (lsr & UART_LSR_BRK_ERROR_BITS) { - if (lsr & UART_LSR_BI) { - tty_flag = TTY_BREAK; - port->icount.brk++; - usb_serial_handle_break(port); - } else if (lsr & UART_LSR_PE) { - tty_flag = TTY_PARITY; - port->icount.parity++; - } else if (lsr & UART_LSR_FE) { - tty_flag = TTY_FRAME; - port->icount.frame++; - } - - if (lsr & UART_LSR_OE) { - port->icount.overrun++; - schedule_work(&priv->lsr_work); - tty_insert_flip_char(&port->port, 0, - TTY_OVERRUN); - } - } + tty_flag = f81232_handle_lsr(port, lsr); if (port->port.console && port->sysrq) { if (usb_serial_handle_sysrq_char(port, data[i + 1])) From 98405f81036d64b4aec770df9850352c7d6e4db5 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 30 Jan 2020 13:47:48 +0800 Subject: [PATCH 0639/2231] USB: serial: f81232: add tx_empty function Add tx_empty() function for F81232. Without this, console redirection will get garbage data. Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Johan Hovold --- drivers/usb/serial/f81232.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index c07d376c743d..793d0b30e347 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -685,6 +685,20 @@ static void f81232_dtr_rts(struct usb_serial_port *port, int on) f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); } +static bool f81232_tx_empty(struct usb_serial_port *port) +{ + int status; + u8 tmp; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp); + if (!status) { + if ((tmp & UART_LSR_TEMT) != UART_LSR_TEMT) + return false; + } + + return true; +} + static int f81232_carrier_raised(struct usb_serial_port *port) { u8 msr; @@ -820,6 +834,7 @@ static struct usb_serial_driver f81232_device = { .tiocmget = f81232_tiocmget, .tiocmset = f81232_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, + .tx_empty = f81232_tx_empty, .process_read_urb = f81232_process_read_urb, .read_int_callback = f81232_read_int_callback, .port_probe = f81232_port_probe, From c4b8f9713d661db605ef0c077ad88ebab427dfef Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 30 Jan 2020 13:47:49 +0800 Subject: [PATCH 0640/2231] USB: serial: f81232: use devm_kzalloc for port data Use devm_kzalloc() to replace kzalloc() in port_probe(). Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Johan Hovold --- drivers/usb/serial/f81232.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 793d0b30e347..e080d678b0c2 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -753,7 +753,7 @@ static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&port->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -769,16 +769,6 @@ static int f81232_port_probe(struct usb_serial_port *port) return 0; } -static int f81232_port_remove(struct usb_serial_port *port) -{ - struct f81232_private *priv; - - priv = usb_get_serial_port_data(port); - kfree(priv); - - return 0; -} - static int f81232_suspend(struct usb_serial *serial, pm_message_t message) { struct usb_serial_port *port = serial->port[0]; @@ -838,7 +828,6 @@ static struct usb_serial_driver f81232_device = { .process_read_urb = f81232_process_read_urb, .read_int_callback = f81232_read_int_callback, .port_probe = f81232_port_probe, - .port_remove = f81232_port_remove, .suspend = f81232_suspend, .resume = f81232_resume, }; From 11301d41ab9a043c20e9b1b79f8754a15dc21ef0 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 30 Jan 2020 13:47:50 +0800 Subject: [PATCH 0641/2231] USB: serial: f81232: add F81534A support The Fintek F81532A/534A/535/536 is USB-to-2/4/8/12 serial ports device and the serial port is default disabled when plugin computer. The IC is contains devices as following: 1. HUB (all devices is connected with this hub) 2. GPIO/Control device. (enable serial port and control GPIOs) 3. serial port 1 to x (2/4/8/12) It's most same with F81232, the UART device is difference as follow: 1. TX/RX bulk size is 128/512bytes 2. RX bulk layout change: F81232: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... F81534A:[LEN][Data.....][LSR] Signed-off-by: Ji-Ze Hong (Peter Hong) [johan: reword an error message] Signed-off-by: Johan Hovold --- drivers/usb/serial/f81232.c | 128 ++++++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index e080d678b0c2..7200771897ed 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Fintek F81232 USB to serial adaptor driver + * Fintek F81532A/534A/535/536 USB to 2/4/8/12 serial adaptor driver * * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org) * Copyright (C) 2012 Linux Foundation @@ -21,11 +22,36 @@ #include #include -static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x1934, 0x0706) }, +#define F81232_ID \ + { USB_DEVICE(0x1934, 0x0706) } /* 1 port UART device */ + +#define F81534A_SERIES_ID \ + { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ \ + { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */ + +static const struct usb_device_id f81232_id_table[] = { + F81232_ID, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE(usb, id_table); + +static const struct usb_device_id f81534a_id_table[] = { + F81534A_SERIES_ID, + { } /* Terminating entry */ +}; + +static const struct usb_device_id combined_id_table[] = { + F81232_ID, + F81534A_SERIES_ID, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, combined_id_table); /* Maximum baudrate for F81232 */ #define F81232_MAX_BAUDRATE 1500000 @@ -61,6 +87,11 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0)) #define F81232_CLK_MASK GENMASK(1, 0) +#define F81534A_MODE_REG 0x107 +#define F81534A_TRIGGER_MASK GENMASK(3, 2) +#define F81534A_TRIGGER_MULTIPLE_4X BIT(3) +#define F81534A_FIFO_128BYTE (BIT(1) | BIT(0)) + struct f81232_private { struct mutex lock; u8 modem_control; @@ -383,6 +414,47 @@ static void f81232_process_read_urb(struct urb *urb) tty_flip_buffer_push(&port->port); } +static void f81534a_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + char tty_flag; + unsigned int i; + u8 lsr; + u8 len; + + if (urb->actual_length < 3) { + dev_err(&port->dev, "short message received: %d\n", + urb->actual_length); + return; + } + + len = data[0]; + if (len != urb->actual_length) { + dev_err(&port->dev, "malformed message received: %d (%d)\n", + urb->actual_length, len); + return; + } + + /* bulk-in data: [LEN][Data.....][LSR] */ + lsr = data[len - 1]; + tty_flag = f81232_handle_lsr(port, lsr); + + if (port->port.console && port->sysrq) { + for (i = 1; i < len - 1; ++i) { + if (!usb_serial_handle_sysrq_char(port, data[i])) { + tty_insert_flip_char(&port->port, data[i], + tty_flag); + } + } + } else { + tty_insert_flip_string_fixed_flag(&port->port, &data[1], + tty_flag, len - 2); + } + + tty_flip_buffer_push(&port->port); +} + static void f81232_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; @@ -666,6 +738,24 @@ static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port) return 0; } +static int f81534a_open(struct tty_struct *tty, struct usb_serial_port *port) +{ + int status; + u8 mask; + u8 val; + + val = F81534A_TRIGGER_MULTIPLE_4X | F81534A_FIFO_128BYTE; + mask = F81534A_TRIGGER_MASK | F81534A_FIFO_128BYTE; + + status = f81232_set_mask_register(port, F81534A_MODE_REG, mask, val); + if (status) { + dev_err(&port->dev, "failed to set MODE_REG: %d\n", status); + return status; + } + + return f81232_open(tty, port); +} + static void f81232_close(struct usb_serial_port *port) { struct f81232_private *port_priv = usb_get_serial_port_data(port); @@ -810,7 +900,7 @@ static struct usb_serial_driver f81232_device = { .owner = THIS_MODULE, .name = "f81232", }, - .id_table = id_table, + .id_table = f81232_id_table, .num_ports = 1, .bulk_in_size = 256, .bulk_out_size = 256, @@ -832,14 +922,40 @@ static struct usb_serial_driver f81232_device = { .resume = f81232_resume, }; +static struct usb_serial_driver f81534a_device = { + .driver = { + .owner = THIS_MODULE, + .name = "f81534a", + }, + .id_table = f81534a_id_table, + .num_ports = 1, + .open = f81534a_open, + .close = f81232_close, + .dtr_rts = f81232_dtr_rts, + .carrier_raised = f81232_carrier_raised, + .get_serial = f81232_get_serial_info, + .break_ctl = f81232_break_ctl, + .set_termios = f81232_set_termios, + .tiocmget = f81232_tiocmget, + .tiocmset = f81232_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, + .tx_empty = f81232_tx_empty, + .process_read_urb = f81534a_process_read_urb, + .read_int_callback = f81232_read_int_callback, + .port_probe = f81232_port_probe, + .suspend = f81232_suspend, + .resume = f81232_resume, +}; + static struct usb_serial_driver * const serial_drivers[] = { &f81232_device, + &f81534a_device, NULL, }; -module_usb_serial_driver(serial_drivers, id_table); +module_usb_serial_driver(serial_drivers, combined_id_table); -MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver"); +MODULE_DESCRIPTION("Fintek F81232/532A/534A/535/536 USB to serial driver"); MODULE_AUTHOR("Greg Kroah-Hartman "); MODULE_AUTHOR("Peter Hong "); MODULE_LICENSE("GPL v2"); From 0a68ec3d8a2ce75666b813b90cbf1d2e85684714 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Thu, 30 Jan 2020 13:47:51 +0800 Subject: [PATCH 0642/2231] USB: serial: f81232: set F81534A serial port with RS232 mode The Fintek F81532A/534A/535/536 is USB-to-2/4/8/12 serial ports device and the serial ports are default disabled. Each port contains max 3 pins GPIO and the 3 pins are default pull high with input mode. When the serial port had activated (running probe()), we'll transform the 3 pins from GPIO function publicly to control Tranceiver privately use. We'll default set to 0/0/1 for control transceiver to RS232 mode. Otherwise, If the serial port is not active, the 3 pins is in GPIO mode and controlled by global GPIO device with VID/PID: 2c42/16f8. Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Johan Hovold --- drivers/usb/serial/f81232.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 7200771897ed..d27876e64e9d 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -92,6 +92,15 @@ MODULE_DEVICE_TABLE(usb, combined_id_table); #define F81534A_TRIGGER_MULTIPLE_4X BIT(3) #define F81534A_FIFO_128BYTE (BIT(1) | BIT(0)) +/* Serial port self GPIO control, 2bytes [control&output data][input data] */ +#define F81534A_GPIO_REG 0x10e +#define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */ +#define F81534A_GPIO_MODE1_DIR BIT(5) +#define F81534A_GPIO_MODE0_DIR BIT(4) +#define F81534A_GPIO_MODE2_OUTPUT BIT(2) +#define F81534A_GPIO_MODE1_OUTPUT BIT(1) +#define F81534A_GPIO_MODE0_OUTPUT BIT(0) + struct f81232_private { struct mutex lock; u8 modem_control; @@ -859,6 +868,19 @@ static int f81232_port_probe(struct usb_serial_port *port) return 0; } +static int f81534a_port_probe(struct usb_serial_port *port) +{ + int status; + + /* tri-state with pull-high, default RS232 Mode */ + status = f81232_set_register(port, F81534A_GPIO_REG, + F81534A_GPIO_MODE2_DIR); + if (status) + return status; + + return f81232_port_probe(port); +} + static int f81232_suspend(struct usb_serial *serial, pm_message_t message) { struct usb_serial_port *port = serial->port[0]; @@ -942,7 +964,7 @@ static struct usb_serial_driver f81534a_device = { .tx_empty = f81232_tx_empty, .process_read_urb = f81534a_process_read_urb, .read_int_callback = f81232_read_int_callback, - .port_probe = f81232_port_probe, + .port_probe = f81534a_port_probe, .suspend = f81232_suspend, .resume = f81232_resume, }; From 8a57d6d4ddfa41c49014e20493152c41a38fcbf8 Mon Sep 17 00:00:00 2001 From: chenqiwu Date: Fri, 7 Feb 2020 17:46:39 +0800 Subject: [PATCH 0643/2231] pstore/platform: fix potential mem leak if pstore_init_fs failed There is a potential mem leak when pstore_init_fs failed, since the pstore compression maybe unlikey to initialized successfully. We must clean up the allocation once this unlikey issue happens. Signed-off-by: chenqiwu Link: https://lore.kernel.org/r/1581068800-13817-1-git-send-email-qiwuchen55@gmail.com Signed-off-by: Kees Cook --- fs/pstore/platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index d896457e7c11..408277ee3cdb 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -823,9 +823,9 @@ static int __init pstore_init(void) ret = pstore_init_fs(); if (ret) - return ret; + free_buf_for_compression(); - return 0; + return ret; } late_initcall(pstore_init); From e030b80ff4a587ba223eee8ac06df214483dba25 Mon Sep 17 00:00:00 2001 From: chenqiwu Date: Fri, 7 Feb 2020 17:46:40 +0800 Subject: [PATCH 0644/2231] pstore/ram: remove unnecessary ramoops_unregister_dummy() Remove unnecessary ramoops_unregister_dummy() if ramoops platform device register failed. Signed-off-by: chenqiwu Link: https://lore.kernel.org/r/1581068800-13817-2-git-send-email-qiwuchen55@gmail.com Signed-off-by: Kees Cook --- fs/pstore/ram.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 013486b5125e..795622190c01 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -963,7 +963,6 @@ static void __init ramoops_register_dummy(void) pr_info("could not create platform device: %ld\n", PTR_ERR(dummy)); dummy = NULL; - ramoops_unregister_dummy(); } } From a67de48b3075cbb6ec030205d7b78981def6596d Mon Sep 17 00:00:00 2001 From: Vincenzo Frascino Date: Mon, 24 Feb 2020 15:15:52 +0000 Subject: [PATCH 0645/2231] clocksource/drivers/arm_arch_timer: Fix vDSO clockmode when vDSO disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The arm_arch_timer requires VDSO_CLOCKMODE_ARCHTIMER to be defined to compile correctly. On ARM the vDSO can be disabled and when this is the case the compilation ends prematurely with an error: $ make ARCH=arm multi_v7_defconfig $ ./scripts/config -d VDSO $ make drivers/clocksource/arm_arch_timer.c:73:44: error: ‘VDSO_CLOCKMODE_ARCHTIMER’ undeclared here (not in a function) static enum vdso_clock_mode vdso_default = VDSO_CLOCKMODE_ARCHTIMER; Make the usage of VDSO_CLOCKMODE_ARCHTIMER depend on the VDSO enablement and initialize the vdso clockmode variable with VDSO_CLOCKMODE_NONE otherwise. [ tglx: Match changelog and patch content. ] Fixes: 5e3c6a312a09 ("ARM/arm64: vdso: Use common vdso clock mode storage") Reported-by: Marek Szyprowski Signed-off-by: Vincenzo Frascino Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20200224151552.57274-1-vincenzo.frascino@arm.com --- drivers/clocksource/arm_arch_timer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index ee2420d56f67..d53f4c7ccaae 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -69,7 +69,11 @@ static enum arch_timer_ppi_nr arch_timer_uses_ppi = ARCH_TIMER_VIRT_PPI; static bool arch_timer_c3stop; static bool arch_timer_mem_use_virtual; static bool arch_counter_suspend_stop; +#ifdef CONFIG_GENERIC_GETTIMEOFDAY static enum vdso_clock_mode vdso_default = VDSO_CLOCKMODE_ARCHTIMER; +#else +static enum vdso_clock_mode vdso_default = VDSO_CLOCKMODE_NONE; +#endif /* CONFIG_GENERIC_GETTIMEOFDAY */ static cpumask_t evtstrm_available = CPU_MASK_NONE; static bool evtstrm_enable = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM); From b5f25304aece9f2e7eaab275bbb5461c666bf38c Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Wed, 19 Feb 2020 17:37:11 +0100 Subject: [PATCH 0646/2231] regulator: add smb208 support Smb208 regulators are used on some ipq806x soc. Add support for it to make it avaiable on some routers that use it. Signed-off-by: Ansuel Smith Signed-off-by: Adrian Panella Acked-by: Lee Jones Link: https://lore.kernel.org/r/20200219163711.479-1-ansuelsmth@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 4 ++++ drivers/regulator/qcom_rpm-regulator.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt index 3c91ad430eea..b823b8625243 100644 --- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt +++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt @@ -61,6 +61,7 @@ Regulator nodes are identified by their compatible: "qcom,rpm-pm8901-regulators" "qcom,rpm-pm8921-regulators" "qcom,rpm-pm8018-regulators" + "qcom,rpm-smb208-regulators" - vdd_l0_l1_lvs-supply: - vdd_l2_l11_l12-supply: @@ -171,6 +172,9 @@ pm8018: s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l14, lvs1 +smb208: + s1a, s1b, s2a, s2b + The content of each sub-node is defined by the standard binding for regulators - see regulator.txt - with additional custom properties described below: diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c index 7407cd5a1b74..7fc97f23fcf4 100644 --- a/drivers/regulator/qcom_rpm-regulator.c +++ b/drivers/regulator/qcom_rpm-regulator.c @@ -925,12 +925,21 @@ static const struct rpm_regulator_data rpm_pm8921_regulators[] = { { } }; +static const struct rpm_regulator_data rpm_smb208_regulators[] = { + { "s1a", QCOM_RPM_SMB208_S1a, &smb208_smps, "vin_s1a" }, + { "s1b", QCOM_RPM_SMB208_S1b, &smb208_smps, "vin_s1b" }, + { "s2a", QCOM_RPM_SMB208_S2a, &smb208_smps, "vin_s2a" }, + { "s2b", QCOM_RPM_SMB208_S2b, &smb208_smps, "vin_s2b" }, + { } +}; + static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pm8018-regulators", .data = &rpm_pm8018_regulators }, { .compatible = "qcom,rpm-pm8058-regulators", .data = &rpm_pm8058_regulators }, { .compatible = "qcom,rpm-pm8901-regulators", .data = &rpm_pm8901_regulators }, { .compatible = "qcom,rpm-pm8921-regulators", .data = &rpm_pm8921_regulators }, + { .compatible = "qcom,rpm-smb208-regulators", .data = &rpm_smb208_regulators }, { } }; MODULE_DEVICE_TABLE(of, rpm_of_match); From ca7b72b5a5f248b72c88441a93bdcee22f42b9b3 Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Wed, 12 Feb 2020 15:04:08 -0300 Subject: [PATCH 0647/2231] clocksource: Add driver for the Ingenic JZ47xx OST OST is the OS Timer, a 64-bit timer/counter with buffered reading. SoCs before the JZ4770 had (if any) a 32-bit OST; the JZ4770 and JZ4780 have a 64-bit OST. This driver will register both a clocksource and a sched_clock to the system. Signed-off-by: Maarten ter Huurne Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Tested-by: Artur Rojek Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200212180408.30872-1-paul@crapouillou.net --- drivers/clocksource/Kconfig | 8 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/ingenic-ost.c | 189 ++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 drivers/clocksource/ingenic-ost.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index cc909e465823..f2142e6bbea3 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -697,6 +697,14 @@ config INGENIC_TIMER help Support for the timer/counter unit of the Ingenic JZ SoCs. +config INGENIC_OST + bool "Clocksource for Ingenic OS Timer" + depends on MIPS || COMPILE_TEST + depends on COMMON_CLK + select MFD_SYSCON + help + Support for the Operating System Timer of the Ingenic JZ SoCs. + config MICROCHIP_PIT64B bool "Microchip PIT64B support" depends on OF || COMPILE_TEST diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 713686faa549..641ba5383ab5 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o obj-$(CONFIG_H8300_TPU) += h8300_tpu.o +obj-$(CONFIG_INGENIC_OST) += ingenic-ost.o obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o diff --git a/drivers/clocksource/ingenic-ost.c b/drivers/clocksource/ingenic-ost.c new file mode 100644 index 000000000000..029efc2731b4 --- /dev/null +++ b/drivers/clocksource/ingenic-ost.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * JZ47xx SoCs TCU Operating System Timer driver + * + * Copyright (C) 2016 Maarten ter Huurne + * Copyright (C) 2020 Paul Cercueil + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TCU_OST_TCSR_MASK 0xffc0 +#define TCU_OST_TCSR_CNT_MD BIT(15) + +#define TCU_OST_CHANNEL 15 + +/* + * The TCU_REG_OST_CNT{L,R} from are only for the + * regmap; these are for use with the __iomem pointer. + */ +#define OST_REG_CNTL 0x4 +#define OST_REG_CNTH 0x8 + +struct ingenic_ost_soc_info { + bool is64bit; +}; + +struct ingenic_ost { + void __iomem *regs; + struct clk *clk; + + struct clocksource cs; +}; + +static struct ingenic_ost *ingenic_ost; + +static u64 notrace ingenic_ost_read_cntl(void) +{ + /* Read using __iomem pointer instead of regmap to avoid locking */ + return readl(ingenic_ost->regs + OST_REG_CNTL); +} + +static u64 notrace ingenic_ost_read_cnth(void) +{ + /* Read using __iomem pointer instead of regmap to avoid locking */ + return readl(ingenic_ost->regs + OST_REG_CNTH); +} + +static u64 notrace ingenic_ost_clocksource_readl(struct clocksource *cs) +{ + return ingenic_ost_read_cntl(); +} + +static u64 notrace ingenic_ost_clocksource_readh(struct clocksource *cs) +{ + return ingenic_ost_read_cnth(); +} + +static int __init ingenic_ost_probe(struct platform_device *pdev) +{ + const struct ingenic_ost_soc_info *soc_info; + struct device *dev = &pdev->dev; + struct ingenic_ost *ost; + struct clocksource *cs; + struct regmap *map; + unsigned long rate; + int err; + + soc_info = device_get_match_data(dev); + if (!soc_info) + return -EINVAL; + + ost = devm_kzalloc(dev, sizeof(*ost), GFP_KERNEL); + if (!ost) + return -ENOMEM; + + ingenic_ost = ost; + + ost->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ost->regs)) + return PTR_ERR(ost->regs); + + map = device_node_to_regmap(dev->parent->of_node); + if (!map) { + dev_err(dev, "regmap not found"); + return -EINVAL; + } + + ost->clk = devm_clk_get(dev, "ost"); + if (IS_ERR(ost->clk)) + return PTR_ERR(ost->clk); + + err = clk_prepare_enable(ost->clk); + if (err) + return err; + + /* Clear counter high/low registers */ + if (soc_info->is64bit) + regmap_write(map, TCU_REG_OST_CNTL, 0); + regmap_write(map, TCU_REG_OST_CNTH, 0); + + /* Don't reset counter at compare value. */ + regmap_update_bits(map, TCU_REG_OST_TCSR, + TCU_OST_TCSR_MASK, TCU_OST_TCSR_CNT_MD); + + rate = clk_get_rate(ost->clk); + + /* Enable OST TCU channel */ + regmap_write(map, TCU_REG_TESR, BIT(TCU_OST_CHANNEL)); + + cs = &ost->cs; + cs->name = "ingenic-ost"; + cs->rating = 320; + cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; + cs->mask = CLOCKSOURCE_MASK(32); + + if (soc_info->is64bit) + cs->read = ingenic_ost_clocksource_readl; + else + cs->read = ingenic_ost_clocksource_readh; + + err = clocksource_register_hz(cs, rate); + if (err) { + dev_err(dev, "clocksource registration failed"); + clk_disable_unprepare(ost->clk); + return err; + } + + if (soc_info->is64bit) + sched_clock_register(ingenic_ost_read_cntl, 32, rate); + else + sched_clock_register(ingenic_ost_read_cnth, 32, rate); + + return 0; +} + +static int __maybe_unused ingenic_ost_suspend(struct device *dev) +{ + struct ingenic_ost *ost = dev_get_drvdata(dev); + + clk_disable(ost->clk); + + return 0; +} + +static int __maybe_unused ingenic_ost_resume(struct device *dev) +{ + struct ingenic_ost *ost = dev_get_drvdata(dev); + + return clk_enable(ost->clk); +} + +static const struct dev_pm_ops __maybe_unused ingenic_ost_pm_ops = { + /* _noirq: We want the OST clock to be gated last / ungated first */ + .suspend_noirq = ingenic_ost_suspend, + .resume_noirq = ingenic_ost_resume, +}; + +static const struct ingenic_ost_soc_info jz4725b_ost_soc_info = { + .is64bit = false, +}; + +static const struct ingenic_ost_soc_info jz4770_ost_soc_info = { + .is64bit = true, +}; + +static const struct of_device_id ingenic_ost_of_match[] = { + { .compatible = "ingenic,jz4725b-ost", .data = &jz4725b_ost_soc_info, }, + { .compatible = "ingenic,jz4770-ost", .data = &jz4770_ost_soc_info, }, + { } +}; + +static struct platform_driver ingenic_ost_driver = { + .driver = { + .name = "ingenic-ost", +#ifdef CONFIG_PM_SUSPEND + .pm = &ingenic_ost_pm_ops, +#endif + .of_match_table = ingenic_ost_of_match, + }, +}; +builtin_platform_driver_probe(ingenic_ost_driver, ingenic_ost_probe); From ad1ded9d2e3d1eb452ff58d325aadf237e187bd9 Mon Sep 17 00:00:00 2001 From: Matheus Castello Date: Tue, 18 Feb 2020 21:48:10 -0300 Subject: [PATCH 0648/2231] clocksource/drivers/owl: Improve owl_timer_init fail messages Check the return from clocksource_mmio_init, add messages in case of an error and in case of not having a defined clock property. Signed-off-by: Matheus Castello Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200219004810.411190-1-matheus@castello.eng.br --- drivers/clocksource/timer-owl.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/timer-owl.c b/drivers/clocksource/timer-owl.c index 900fe736145d..ac97420bfa7c 100644 --- a/drivers/clocksource/timer-owl.c +++ b/drivers/clocksource/timer-owl.c @@ -135,8 +135,11 @@ static int __init owl_timer_init(struct device_node *node) } clk = of_clk_get(node, 0); - if (IS_ERR(clk)) - return PTR_ERR(clk); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + pr_err("Failed to get clock for clocksource (%d)\n", ret); + return ret; + } rate = clk_get_rate(clk); @@ -144,8 +147,12 @@ static int __init owl_timer_init(struct device_node *node) owl_timer_set_enabled(owl_clksrc_base, true); sched_clock_register(owl_timer_sched_read, 32, rate); - clocksource_mmio_init(owl_clksrc_base + OWL_Tx_VAL, node->name, - rate, 200, 32, clocksource_mmio_readl_up); + ret = clocksource_mmio_init(owl_clksrc_base + OWL_Tx_VAL, node->name, + rate, 200, 32, clocksource_mmio_readl_up); + if (ret) { + pr_err("Failed to register clocksource (%d)\n", ret); + return ret; + } owl_timer_reset(owl_clkevt_base); From 6ce4fcb015a1a1290ffafcf3554901b40f9322df Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Mon, 24 Feb 2020 10:37:53 +0530 Subject: [PATCH 0649/2231] clocksource/drivers/timer-ti-dm: Do not update counter on updating the period Write to trigger register(OMAP_TIMER_TRIGGER_REG) will load the value in Load register(OMAP_TIMER_LOAD_REG) into Counter register (OMAP_TIMER_COUNTER_REG). omap_dm_timer_set_load() writes into trigger register every time load register is updated. When timer is configured in pwm mode, this causes disruption in current pwm cycle, which is not expected especially when pwm is used as PPS signal for synchronized PTP clocks. So do not write into trigger register on updating the period. Signed-off-by: Lokesh Vutla Tested-by: Tony Lindgren Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200224050753.17784-3-lokeshvutla@ti.com --- drivers/clocksource/timer-ti-dm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 269a994d6a99..acc93600d351 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -577,7 +577,6 @@ static int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); - omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); /* Save the context */ timer->context.tclr = l; timer->context.tldr = load; From 753e83408b7f2980b7a5bfcf01f1175a937a2340 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Wed, 12 Feb 2020 23:35:04 -0600 Subject: [PATCH 0650/2231] clocksource/drivers/timer-ti-dm: Drop bogus omap_dm_timer_of_set_source() The function omap_dm_timer_of_set_source() was originally added in commit 31a7448f4fa8a ("ARM: OMAP: dmtimer: Add clock source from DT"), and is designed to set a clock source from DT using the clocks property of a timer node. This design choice is okay for clk provider nodes but otherwise is a bad design as typically the clocks property is used to specify the functional clocks for a device, and not its parents. The timer nodes now all define a timer functional clock after the conversion to ti-sysc and the new clkctrl layout, and this results in an attempt to set the same functional clock as its parent when a consumer driver attempts to acquire any of these timers in the omap_dm_timer_prepare() function. This was masked and worked around in commit 983a5a43ec25 ("clocksource: timer-ti-dm: Fix pwm dmtimer usage of fck reparenting"). Fix all of this by simply dropping the entire function. Any DT configuration of clock sources should be achieved using assigned-clocks and assigned-clock-parents properties provided by the Common Clock Framework. Cc: Tony Lindgren Cc: Tero Kristo Cc: Neil Armstrong Cc: H. Nikolaus Schaller Cc: Bartosz Golaszewski Cc: Keerthy Cc: Ladislav Michl Cc: Pavel Machek Cc: Sebastian Reichel Signed-off-by: Suman Anna Tested-by: Lokesh Vutla Tested-by: Tony Lindgren Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20200213053504.22638-1-s-anna@ti.com --- drivers/clocksource/timer-ti-dm.c | 33 +------------------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index acc93600d351..6a0adb7104b3 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -138,35 +138,6 @@ static int omap_dm_timer_reset(struct omap_dm_timer *timer) return 0; } -static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer) -{ - int ret; - struct clk *parent; - - /* - * FIXME: OMAP1 devices do not use the clock framework for dmtimers so - * do not call clk_get() for these devices. - */ - if (!timer->fclk) - return -ENODEV; - - parent = clk_get(&timer->pdev->dev, NULL); - if (IS_ERR(parent)) - return -ENODEV; - - /* Bail out if both clocks point to fck */ - if (clk_is_match(parent, timer->fclk)) - return 0; - - ret = clk_set_parent(timer->fclk, parent); - if (ret < 0) - pr_err("%s: failed to set parent\n", __func__); - - clk_put(parent); - - return ret; -} - static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) { int ret; @@ -276,9 +247,7 @@ static int omap_dm_timer_prepare(struct omap_dm_timer *timer) __omap_dm_timer_enable_posted(timer); omap_dm_timer_disable(timer); - rc = omap_dm_timer_of_set_source(timer); - if (rc == -ENODEV) - return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); + rc = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); return rc; } From fe6c2d6a80680a875a856eb174d12acea7681247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=90=B0=E6=9D=B0=20=28Zhou=20Yanjie=29?= Date: Wed, 19 Feb 2020 16:29:31 +0800 Subject: [PATCH 0651/2231] dt-bindings: timer: Add X1000 bindings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the timer bindings for the X1000 Soc from Ingenic. Signed-off-by: 周琰杰 (Zhou Yanjie) Acked-by: Rob Herring Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1582100974-129559-3-git-send-email-zhouyanjie@wanyeetech.com --- Documentation/devicetree/bindings/timer/ingenic,tcu.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/timer/ingenic,tcu.txt b/Documentation/devicetree/bindings/timer/ingenic,tcu.txt index 0b63cebc5f45..91f704951845 100644 --- a/Documentation/devicetree/bindings/timer/ingenic,tcu.txt +++ b/Documentation/devicetree/bindings/timer/ingenic,tcu.txt @@ -10,6 +10,7 @@ Required properties: * ingenic,jz4740-tcu * ingenic,jz4725b-tcu * ingenic,jz4770-tcu + * ingenic,x1000-tcu followed by "simple-mfd". - reg: Should be the offset/length value corresponding to the TCU registers - clocks: List of phandle & clock specifiers for clocks external to the TCU. From a7cd39552194954bcdecfd9ff775466a61bda5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=90=B0=E6=9D=B0=20=28Zhou=20Yanjie=29?= Date: Wed, 19 Feb 2020 16:29:33 +0800 Subject: [PATCH 0652/2231] clocksource/drivers/ingenic: Add support for TCU of X1000 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X1000 has a different TCU containing OST, since X1000, OST has been independent of TCU. This patch is prepare for later OST driver. Signed-off-by: 周琰杰 (Zhou Yanjie) Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1582100974-129559-5-git-send-email-zhouyanjie@wanyeetech.com --- drivers/clocksource/ingenic-timer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/ingenic-timer.c b/drivers/clocksource/ingenic-timer.c index 4bbdb3d3d0c6..496333650de2 100644 --- a/drivers/clocksource/ingenic-timer.c +++ b/drivers/clocksource/ingenic-timer.c @@ -230,6 +230,7 @@ static const struct of_device_id ingenic_tcu_of_match[] = { { .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, }, { .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, }, { .compatible = "ingenic,jz4770-tcu", .data = &jz4740_soc_info, }, + { .compatible = "ingenic,x1000-tcu", .data = &jz4740_soc_info, }, { /* sentinel */ } }; @@ -302,7 +303,7 @@ err_free_ingenic_tcu: TIMER_OF_DECLARE(jz4740_tcu_intc, "ingenic,jz4740-tcu", ingenic_tcu_init); TIMER_OF_DECLARE(jz4725b_tcu_intc, "ingenic,jz4725b-tcu", ingenic_tcu_init); TIMER_OF_DECLARE(jz4770_tcu_intc, "ingenic,jz4770-tcu", ingenic_tcu_init); - +TIMER_OF_DECLARE(x1000_tcu_intc, "ingenic,x1000-tcu", ingenic_tcu_init); static int __init ingenic_tcu_probe(struct platform_device *pdev) { From cc2550b421aa30e3da67e5a7f6d14f3ecd3527b3 Mon Sep 17 00:00:00 2001 From: afzal mohammed Date: Thu, 27 Feb 2020 16:29:02 +0530 Subject: [PATCH 0653/2231] clocksource: Replace setup_irq() by request_irq() request_irq() is preferred over setup_irq(). The early boot setup_irq() invocations happen either via 'init_IRQ()' or 'time_init()', while memory allocators are ready by 'mm_init()'. Per tglx[1], setup_irq() existed in olden days when allocators were not ready by the time early interrupts were initialized. Hence replace setup_irq() by request_irq(). Seldom remove_irq() usage has been observed coupled with setup_irq(), wherever that has been found, it too has been replaced by free_irq(). A build error that was reported by kbuild test robot in the previous version of the patch also has been fixed. [1] https://lkml.kernel.org/r/alpine.DEB.2.20.1710191609480.1971@nanos Signed-off-by: afzal mohammed Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/91961c77c1cf93d41523f5e1ac52043f32f97077.1582799709.git.afzal.mohd.ma@gmail.com --- drivers/clocksource/bcm2835_timer.c | 8 +--- drivers/clocksource/bcm_kona_timer.c | 10 ++--- drivers/clocksource/dw_apb_timer.c | 11 ++---- drivers/clocksource/exynos_mct.c | 12 ++---- drivers/clocksource/mxs_timer.c | 10 +---- drivers/clocksource/nomadik-mtu.c | 11 ++---- drivers/clocksource/samsung_pwm_timer.c | 12 ++---- drivers/clocksource/timer-atlas7.c | 48 +++++++++++------------ drivers/clocksource/timer-cs5535.c | 10 ++--- drivers/clocksource/timer-efm32.c | 10 +---- drivers/clocksource/timer-fsl-ftm.c | 10 +---- drivers/clocksource/timer-imx-gpt.c | 10 +---- drivers/clocksource/timer-integrator-ap.c | 11 ++---- drivers/clocksource/timer-meson6.c | 11 ++---- drivers/clocksource/timer-orion.c | 9 +---- drivers/clocksource/timer-prima2.c | 14 ++----- drivers/clocksource/timer-pxa.c | 10 +---- drivers/clocksource/timer-sp804.c | 11 ++---- drivers/clocksource/timer-u300.c | 9 +---- drivers/clocksource/timer-vf-pit.c | 10 +---- drivers/clocksource/timer-vt8500.c | 11 ++---- drivers/clocksource/timer-zevio.c | 13 +++--- include/linux/dw_apb_timer.h | 1 - 23 files changed, 82 insertions(+), 190 deletions(-) diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c index b235f446ee50..1592650b2c92 100644 --- a/drivers/clocksource/bcm2835_timer.c +++ b/drivers/clocksource/bcm2835_timer.c @@ -31,7 +31,6 @@ struct bcm2835_timer { void __iomem *compare; int match_mask; struct clock_event_device evt; - struct irqaction act; }; static void __iomem *system_clock __read_mostly; @@ -113,12 +112,9 @@ static int __init bcm2835_timer_init(struct device_node *node) timer->evt.features = CLOCK_EVT_FEAT_ONESHOT; timer->evt.set_next_event = bcm2835_time_set_next_event; timer->evt.cpumask = cpumask_of(0); - timer->act.name = node->name; - timer->act.flags = IRQF_TIMER | IRQF_SHARED; - timer->act.dev_id = timer; - timer->act.handler = bcm2835_time_interrupt; - ret = setup_irq(irq, &timer->act); + ret = request_irq(irq, bcm2835_time_interrupt, IRQF_TIMER | IRQF_SHARED, + node->name, timer); if (ret) { pr_err("Can't set up timer IRQ\n"); goto err_timer_free; diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c index 5c40be9880f5..a50ab5c2154f 100644 --- a/drivers/clocksource/bcm_kona_timer.c +++ b/drivers/clocksource/bcm_kona_timer.c @@ -160,12 +160,6 @@ static irqreturn_t kona_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction kona_timer_irq = { - .name = "Kona Timer Tick", - .flags = IRQF_TIMER, - .handler = kona_timer_interrupt, -}; - static int __init kona_timer_init(struct device_node *node) { u32 freq; @@ -192,7 +186,9 @@ static int __init kona_timer_init(struct device_node *node) kona_timer_disable_and_clear(timers.tmr_regs); kona_timer_clockevents_init(); - setup_irq(timers.tmr_irq, &kona_timer_irq); + if (request_irq(timers.tmr_irq, kona_timer_interrupt, IRQF_TIMER, + "Kona Timer Tick", NULL)) + pr_err("%s: request_irq() failed\n", "Kona Timer Tick"); kona_timer_set_next_event((arch_timer_rate / HZ), NULL); return 0; diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c index 654766538f93..b207a77b0831 100644 --- a/drivers/clocksource/dw_apb_timer.c +++ b/drivers/clocksource/dw_apb_timer.c @@ -270,15 +270,10 @@ dw_apb_clockevent_init(int cpu, const char *name, unsigned rating, dw_ced->ced.rating = rating; dw_ced->ced.name = name; - dw_ced->irqaction.name = dw_ced->ced.name; - dw_ced->irqaction.handler = dw_apb_clockevent_irq; - dw_ced->irqaction.dev_id = &dw_ced->ced; - dw_ced->irqaction.irq = irq; - dw_ced->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | - IRQF_NOBALANCING; - dw_ced->eoi = apbt_eoi; - err = setup_irq(irq, &dw_ced->irqaction); + err = request_irq(irq, dw_apb_clockevent_irq, + IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, + dw_ced->ced.name, &dw_ced->ced); if (err) { pr_err("failed to request timer irq\n"); kfree(dw_ced); diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index a267fe31ef13..fabad79baafc 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -329,19 +329,15 @@ static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction mct_comp_event_irq = { - .name = "mct_comp_irq", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = exynos4_mct_comp_isr, - .dev_id = &mct_comp_device, -}; - static int exynos4_clockevent_init(void) { mct_comp_device.cpumask = cpumask_of(0); clockevents_config_and_register(&mct_comp_device, clk_rate, 0xf, 0xffffffff); - setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq); + if (request_irq(mct_irqs[MCT_G0_IRQ], exynos4_mct_comp_isr, + IRQF_TIMER | IRQF_IRQPOLL, "mct_comp_irq", + &mct_comp_device)) + pr_err("%s: request_irq() failed\n", "mct_comp_irq"); return 0; } diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c index f6ddae30933f..bc96a4cbf26c 100644 --- a/drivers/clocksource/mxs_timer.c +++ b/drivers/clocksource/mxs_timer.c @@ -117,13 +117,6 @@ static irqreturn_t mxs_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction mxs_timer_irq = { - .name = "MXS Timer Tick", - .dev_id = &mxs_clockevent_device, - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = mxs_timer_interrupt, -}; - static void mxs_irq_clear(char *state) { /* Disable interrupt in timer module */ @@ -277,6 +270,7 @@ static int __init mxs_timer_init(struct device_node *np) if (irq <= 0) return -EINVAL; - return setup_irq(irq, &mxs_timer_irq); + return request_irq(irq, mxs_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "MXS Timer Tick", &mxs_clockevent_device); } TIMER_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init); diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c index 3f7fa8c01367..f49a631d8f58 100644 --- a/drivers/clocksource/nomadik-mtu.c +++ b/drivers/clocksource/nomadik-mtu.c @@ -181,13 +181,6 @@ static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction nmdk_timer_irq = { - .name = "Nomadik Timer Tick", - .flags = IRQF_TIMER, - .handler = nmdk_timer_interrupt, - .dev_id = &nmdk_clkevt, -}; - static int __init nmdk_timer_init(void __iomem *base, int irq, struct clk *pclk, struct clk *clk) { @@ -232,7 +225,9 @@ static int __init nmdk_timer_init(void __iomem *base, int irq, sched_clock_register(nomadik_read_sched_clock, 32, rate); /* Timer 1 is used for events, register irq and clockevents */ - setup_irq(irq, &nmdk_timer_irq); + if (request_irq(irq, nmdk_timer_interrupt, IRQF_TIMER, + "Nomadik Timer Tick", &nmdk_clkevt)) + pr_err("%s: request_irq() failed\n", "Nomadik Timer Tick"); nmdk_clkevt.cpumask = cpumask_of(0); nmdk_clkevt.irq = irq; clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU); diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index dae1b2b5a0c5..f760229d0c7f 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -256,13 +256,6 @@ static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction samsung_clock_event_irq = { - .name = "samsung_time_irq", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = samsung_clock_event_isr, - .dev_id = &time_event_device, -}; - static void __init samsung_clockevent_init(void) { unsigned long pclk; @@ -282,7 +275,10 @@ static void __init samsung_clockevent_init(void) clock_rate, 1, pwm.tcnt_max); irq_number = pwm.irq[pwm.event_id]; - setup_irq(irq_number, &samsung_clock_event_irq); + if (request_irq(irq_number, samsung_clock_event_isr, + IRQF_TIMER | IRQF_IRQPOLL, "samsung_time_irq", + &time_event_device)) + pr_err("%s: request_irq() failed\n", "samsung_time_irq"); if (pwm.variant.has_tint_cstat) { u32 mask = (1 << pwm.event_id); diff --git a/drivers/clocksource/timer-atlas7.c b/drivers/clocksource/timer-atlas7.c index 93c3ac6d72bd..c21c91c2bc56 100644 --- a/drivers/clocksource/timer-atlas7.c +++ b/drivers/clocksource/timer-atlas7.c @@ -159,29 +159,23 @@ static struct clocksource sirfsoc_clocksource = { .resume = sirfsoc_clocksource_resume, }; -static struct irqaction sirfsoc_timer_irq = { - .name = "sirfsoc_timer0", - .flags = IRQF_TIMER | IRQF_NOBALANCING, - .handler = sirfsoc_timer_interrupt, -}; - -static struct irqaction sirfsoc_timer1_irq = { - .name = "sirfsoc_timer1", - .flags = IRQF_TIMER | IRQF_NOBALANCING, - .handler = sirfsoc_timer_interrupt, -}; +static unsigned int sirfsoc_timer_irq, sirfsoc_timer1_irq; static int sirfsoc_local_timer_starting_cpu(unsigned int cpu) { struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu); - struct irqaction *action; + unsigned int irq; + const char *name; - if (cpu == 0) - action = &sirfsoc_timer_irq; - else - action = &sirfsoc_timer1_irq; + if (cpu == 0) { + irq = sirfsoc_timer_irq; + name = "sirfsoc_timer0"; + } else { + irq = sirfsoc_timer1_irq; + name = "sirfsoc_timer1"; + } - ce->irq = action->irq; + ce->irq = irq; ce->name = "local_timer"; ce->features = CLOCK_EVT_FEAT_ONESHOT; ce->rating = 200; @@ -196,9 +190,9 @@ static int sirfsoc_local_timer_starting_cpu(unsigned int cpu) ce->min_delta_ticks = 2; ce->cpumask = cpumask_of(cpu); - action->dev_id = ce; - BUG_ON(setup_irq(ce->irq, action)); - irq_force_affinity(action->irq, cpumask_of(cpu)); + BUG_ON(request_irq(ce->irq, sirfsoc_timer_interrupt, + IRQF_TIMER | IRQF_NOBALANCING, name, ce)); + irq_force_affinity(ce->irq, cpumask_of(cpu)); clockevents_register_device(ce); return 0; @@ -206,12 +200,14 @@ static int sirfsoc_local_timer_starting_cpu(unsigned int cpu) static int sirfsoc_local_timer_dying_cpu(unsigned int cpu) { + struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu); + sirfsoc_timer_count_disable(1); if (cpu == 0) - remove_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq); + free_irq(sirfsoc_timer_irq, ce); else - remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq); + free_irq(sirfsoc_timer1_irq, ce); return 0; } @@ -268,14 +264,14 @@ static int __init sirfsoc_of_timer_init(struct device_node *np) return -ENXIO; } - sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); - if (!sirfsoc_timer_irq.irq) { + sirfsoc_timer_irq = irq_of_parse_and_map(np, 0); + if (!sirfsoc_timer_irq) { pr_err("No irq passed for timer0 via DT\n"); return -EINVAL; } - sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1); - if (!sirfsoc_timer1_irq.irq) { + sirfsoc_timer1_irq = irq_of_parse_and_map(np, 1); + if (!sirfsoc_timer1_irq) { pr_err("No irq passed for timer1 via DT\n"); return -EINVAL; } diff --git a/drivers/clocksource/timer-cs5535.c b/drivers/clocksource/timer-cs5535.c index 8f6bc536bef2..51ea0509fb25 100644 --- a/drivers/clocksource/timer-cs5535.c +++ b/drivers/clocksource/timer-cs5535.c @@ -131,12 +131,6 @@ static irqreturn_t mfgpt_tick(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction mfgptirq = { - .handler = mfgpt_tick, - .flags = IRQF_NOBALANCING | IRQF_TIMER | IRQF_SHARED, - .name = DRV_NAME, -}; - static int __init cs5535_mfgpt_init(void) { struct cs5535_mfgpt_timer *timer; @@ -158,7 +152,9 @@ static int __init cs5535_mfgpt_init(void) } /* And register it with the kernel */ - ret = setup_irq(timer_irq, &mfgptirq); + ret = request_irq(timer_irq, mfgpt_tick, + IRQF_NOBALANCING | IRQF_TIMER | IRQF_SHARED, + DRV_NAME, NULL); if (ret) { printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n"); goto err_irq; diff --git a/drivers/clocksource/timer-efm32.c b/drivers/clocksource/timer-efm32.c index 5a22cb079ad3..441a4b916841 100644 --- a/drivers/clocksource/timer-efm32.c +++ b/drivers/clocksource/timer-efm32.c @@ -119,13 +119,6 @@ static struct efm32_clock_event_ddata clock_event_ddata = { }, }; -static struct irqaction efm32_clock_event_irq = { - .name = "efm32 clockevent", - .flags = IRQF_TIMER, - .handler = efm32_clock_event_handler, - .dev_id = &clock_event_ddata, -}; - static int __init efm32_clocksource_init(struct device_node *np) { struct clk *clk; @@ -230,7 +223,8 @@ static int __init efm32_clockevent_init(struct device_node *np) DIV_ROUND_CLOSEST(rate, 1024), 0xf, 0xffff); - ret = setup_irq(irq, &efm32_clock_event_irq); + ret = request_irq(irq, efm32_clock_event_handler, IRQF_TIMER, + "efm32 clockevent", &clock_event_ddata); if (ret) { pr_err("Failed setup irq\n"); goto err_setup_irq; diff --git a/drivers/clocksource/timer-fsl-ftm.c b/drivers/clocksource/timer-fsl-ftm.c index a9d9a3ca5996..12a2ed7cfaff 100644 --- a/drivers/clocksource/timer-fsl-ftm.c +++ b/drivers/clocksource/timer-fsl-ftm.c @@ -176,13 +176,6 @@ static struct clock_event_device ftm_clockevent = { .rating = 300, }; -static struct irqaction ftm_timer_irq = { - .name = "Freescale ftm timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = ftm_evt_interrupt, - .dev_id = &ftm_clockevent, -}; - static int __init ftm_clockevent_init(unsigned long freq, int irq) { int err; @@ -192,7 +185,8 @@ static int __init ftm_clockevent_init(unsigned long freq, int irq) ftm_reset_counter(priv->clkevt_base); - err = setup_irq(irq, &ftm_timer_irq); + err = request_irq(irq, ftm_evt_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "Freescale ftm timer", &ftm_clockevent); if (err) { pr_err("ftm: setup irq failed: %d\n", err); return err; diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c index 706c0d0ff56c..7b2c70f2f353 100644 --- a/drivers/clocksource/timer-imx-gpt.c +++ b/drivers/clocksource/timer-imx-gpt.c @@ -67,7 +67,6 @@ struct imx_timer { struct clk *clk_ipg; const struct imx_gpt_data *gpt; struct clock_event_device ced; - struct irqaction act; }; struct imx_gpt_data { @@ -273,7 +272,6 @@ static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) static int __init mxc_clockevent_init(struct imx_timer *imxtm) { struct clock_event_device *ced = &imxtm->ced; - struct irqaction *act = &imxtm->act; ced->name = "mxc_timer1"; ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ; @@ -287,12 +285,8 @@ static int __init mxc_clockevent_init(struct imx_timer *imxtm) clockevents_config_and_register(ced, clk_get_rate(imxtm->clk_per), 0xff, 0xfffffffe); - act->name = "i.MX Timer Tick"; - act->flags = IRQF_TIMER | IRQF_IRQPOLL; - act->handler = mxc_timer_interrupt; - act->dev_id = ced; - - return setup_irq(imxtm->irq, act); + return request_irq(imxtm->irq, mxc_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "i.MX Timer Tick", ced); } static void imx1_gpt_setup_tctl(struct imx_timer *imxtm) diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c index c90a69c7b5fa..b0fcbaac58b0 100644 --- a/drivers/clocksource/timer-integrator-ap.c +++ b/drivers/clocksource/timer-integrator-ap.c @@ -123,13 +123,6 @@ static struct clock_event_device integrator_clockevent = { .rating = 300, }; -static struct irqaction integrator_timer_irq = { - .name = "timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = integrator_timer_interrupt, - .dev_id = &integrator_clockevent, -}; - static int integrator_clockevent_init(unsigned long inrate, void __iomem *base, int irq) { @@ -149,7 +142,9 @@ static int integrator_clockevent_init(unsigned long inrate, timer_reload = rate / HZ; writel(ctrl, clkevt_base + TIMER_CTRL); - ret = setup_irq(irq, &integrator_timer_irq); + ret = request_irq(irq, integrator_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "timer", + &integrator_clockevent); if (ret) return ret; diff --git a/drivers/clocksource/timer-meson6.c b/drivers/clocksource/timer-meson6.c index 9e8b467c71da..99f5510a2b56 100644 --- a/drivers/clocksource/timer-meson6.c +++ b/drivers/clocksource/timer-meson6.c @@ -150,13 +150,6 @@ static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction meson6_timer_irq = { - .name = "meson6_timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = meson6_timer_interrupt, - .dev_id = &meson6_clockevent, -}; - static int __init meson6_timer_init(struct device_node *node) { u32 val; @@ -194,7 +187,9 @@ static int __init meson6_timer_init(struct device_node *node) /* Stop the timer A */ meson6_clkevt_time_stop(); - ret = setup_irq(irq, &meson6_timer_irq); + ret = request_irq(irq, meson6_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "meson6_timer", + &meson6_clockevent); if (ret) { pr_warn("failed to setup irq %d\n", irq); return ret; diff --git a/drivers/clocksource/timer-orion.c b/drivers/clocksource/timer-orion.c index 7d487107e3cd..d01ff4181867 100644 --- a/drivers/clocksource/timer-orion.c +++ b/drivers/clocksource/timer-orion.c @@ -114,12 +114,6 @@ static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction orion_clkevt_irq = { - .name = "orion_event", - .flags = IRQF_TIMER, - .handler = orion_clkevt_irq_handler, -}; - static int __init orion_timer_init(struct device_node *np) { unsigned long rate; @@ -172,7 +166,8 @@ static int __init orion_timer_init(struct device_node *np) sched_clock_register(orion_read_sched_clock, 32, rate); /* setup timer1 as clockevent timer */ - ret = setup_irq(irq, &orion_clkevt_irq); + ret = request_irq(irq, orion_clkevt_irq_handler, IRQF_TIMER, + "orion_event", NULL); if (ret) { pr_err("%pOFn: unable to setup irq\n", np); return ret; diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c index d4a9dcf5fba2..c5d469342a9d 100644 --- a/drivers/clocksource/timer-prima2.c +++ b/drivers/clocksource/timer-prima2.c @@ -165,14 +165,6 @@ static struct clocksource sirfsoc_clocksource = { .resume = sirfsoc_clocksource_resume, }; -static struct irqaction sirfsoc_timer_irq = { - .name = "sirfsoc_timer0", - .flags = IRQF_TIMER, - .irq = 0, - .handler = sirfsoc_timer_interrupt, - .dev_id = &sirfsoc_clockevent, -}; - /* Overwrite weak default sched_clock with more precise one */ static u64 notrace sirfsoc_read_sched_clock(void) { @@ -190,6 +182,7 @@ static void __init sirfsoc_clockevent_init(void) static int __init sirfsoc_prima2_timer_init(struct device_node *np) { unsigned long rate; + unsigned int irq; struct clk *clk; int ret; @@ -218,7 +211,7 @@ static int __init sirfsoc_prima2_timer_init(struct device_node *np) return -ENXIO; } - sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); + irq = irq_of_parse_and_map(np, 0); writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV); @@ -234,7 +227,8 @@ static int __init sirfsoc_prima2_timer_init(struct device_node *np) sched_clock_register(sirfsoc_read_sched_clock, 64, PRIMA2_CLOCK_FREQ); - ret = setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq); + ret = request_irq(irq, sirfsoc_timer_interrupt, IRQF_TIMER, + "sirfsoc_timer0", &sirfsoc_clockevent); if (ret) { pr_err("Failed to setup irq\n"); return ret; diff --git a/drivers/clocksource/timer-pxa.c b/drivers/clocksource/timer-pxa.c index 913a5d354a1f..7ad0e5adb2ff 100644 --- a/drivers/clocksource/timer-pxa.c +++ b/drivers/clocksource/timer-pxa.c @@ -143,13 +143,6 @@ static struct clock_event_device ckevt_pxa_osmr0 = { .resume = pxa_timer_resume, }; -static struct irqaction pxa_ost0_irq = { - .name = "ost0", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = pxa_ost0_interrupt, - .dev_id = &ckevt_pxa_osmr0, -}; - static int __init pxa_timer_common_init(int irq, unsigned long clock_tick_rate) { int ret; @@ -161,7 +154,8 @@ static int __init pxa_timer_common_init(int irq, unsigned long clock_tick_rate) ckevt_pxa_osmr0.cpumask = cpumask_of(0); - ret = setup_irq(irq, &pxa_ost0_irq); + ret = request_irq(irq, pxa_ost0_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "ost0", &ckevt_pxa_osmr0); if (ret) { pr_err("Failed to setup irq\n"); return ret; diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c index 9c841980eed1..5cd0abf9b396 100644 --- a/drivers/clocksource/timer-sp804.c +++ b/drivers/clocksource/timer-sp804.c @@ -168,13 +168,6 @@ static struct clock_event_device sp804_clockevent = { .rating = 300, }; -static struct irqaction sp804_timer_irq = { - .name = "timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = sp804_timer_interrupt, - .dev_id = &sp804_clockevent, -}; - int __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name) { struct clock_event_device *evt = &sp804_clockevent; @@ -200,7 +193,9 @@ int __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct writel(0, base + TIMER_CTRL); - setup_irq(irq, &sp804_timer_irq); + if (request_irq(irq, sp804_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "timer", &sp804_clockevent)) + pr_err("%s: request_irq() failed\n", "timer"); clockevents_config_and_register(evt, rate, 0xf, 0xffffffff); return 0; diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c index 32adc3057dda..37cba8dfd45f 100644 --- a/drivers/clocksource/timer-u300.c +++ b/drivers/clocksource/timer-u300.c @@ -330,12 +330,6 @@ static irqreturn_t u300_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction u300_timer_irq = { - .name = "U300 Timer Tick", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = u300_timer_interrupt, -}; - /* * Override the global weak sched_clock symbol with this * local implementation which uses the clocksource to get some @@ -420,7 +414,8 @@ static int __init u300_timer_init_of(struct device_node *np) u300_timer_base + U300_TIMER_APP_RGPT1); /* Set up the IRQ handler */ - ret = setup_irq(irq, &u300_timer_irq); + ret = request_irq(irq, u300_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "U300 Timer Tick", NULL); if (ret) return ret; diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index fef0bb4e0c8c..7ad4a8b008c2 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -123,19 +123,13 @@ static struct clock_event_device clockevent_pit = { .rating = 300, }; -static struct irqaction pit_timer_irq = { - .name = "VF pit timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = pit_timer_interrupt, - .dev_id = &clockevent_pit, -}; - static int __init pit_clockevent_init(unsigned long rate, int irq) { __raw_writel(0, clkevt_base + PITTCTRL); __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); - BUG_ON(setup_irq(irq, &pit_timer_irq)); + BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "VF pit timer", &clockevent_pit); clockevent_pit.cpumask = cpumask_of(0); clockevent_pit.irq = irq; diff --git a/drivers/clocksource/timer-vt8500.c b/drivers/clocksource/timer-vt8500.c index bb424bcefbb3..a469b1b5f972 100644 --- a/drivers/clocksource/timer-vt8500.c +++ b/drivers/clocksource/timer-vt8500.c @@ -101,13 +101,6 @@ static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction irq = { - .name = "vt8500_timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = vt8500_timer_interrupt, - .dev_id = &clockevent, -}; - static int __init vt8500_timer_init(struct device_node *np) { int timer_irq, ret; @@ -139,7 +132,9 @@ static int __init vt8500_timer_init(struct device_node *np) clockevent.cpumask = cpumask_of(0); - ret = setup_irq(timer_irq, &irq); + ret = request_irq(timer_irq, vt8500_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "vt8500_timer", + &clockevent); if (ret) { pr_err("%s: setup_irq failed for %s\n", __func__, clockevent.name); diff --git a/drivers/clocksource/timer-zevio.c b/drivers/clocksource/timer-zevio.c index c0041561f1be..ecaa3568841c 100644 --- a/drivers/clocksource/timer-zevio.c +++ b/drivers/clocksource/timer-zevio.c @@ -53,7 +53,6 @@ struct zevio_timer { struct clk *clk; struct clock_event_device clkevt; - struct irqaction clkevt_irq; char clocksource_name[64]; char clockevent_name[64]; @@ -172,12 +171,12 @@ static int __init zevio_timer_add(struct device_node *node) /* Interrupt to occur when timer value matches 0 */ writel(0, timer->base + IO_MATCH(TIMER_MATCH)); - timer->clkevt_irq.name = timer->clockevent_name; - timer->clkevt_irq.handler = zevio_timer_interrupt; - timer->clkevt_irq.dev_id = timer; - timer->clkevt_irq.flags = IRQF_TIMER | IRQF_IRQPOLL; - - setup_irq(irqnr, &timer->clkevt_irq); + if (request_irq(irqnr, zevio_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, + timer->clockevent_name, timer)) { + pr_err("%s: request_irq() failed\n", + timer->clockevent_name); + } clockevents_config_and_register(&timer->clkevt, clk_get_rate(timer->clk), 0x0001, 0xffff); diff --git a/include/linux/dw_apb_timer.h b/include/linux/dw_apb_timer.h index 14f072edbca5..82ebf9223948 100644 --- a/include/linux/dw_apb_timer.h +++ b/include/linux/dw_apb_timer.h @@ -25,7 +25,6 @@ struct dw_apb_timer { struct dw_apb_clock_event_device { struct clock_event_device ced; struct dw_apb_timer timer; - struct irqaction irqaction; void (*eoi)(struct dw_apb_timer *); }; From 3d51507f29f2153a658df4a0674ec5b592b62085 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Feb 2020 22:36:37 +0100 Subject: [PATCH 0654/2231] x86/entry/32: Add missing ASM_CLAC to general_protection entry All exception entry points must have ASM_CLAC right at the beginning. The general_protection entry is missing one. Fixes: e59d1b0a2419 ("x86-32, smap: Add STAC/CLAC instructions to 32-bit kernel entry") Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Reviewed-by: Alexandre Chartre Reviewed-by: Andy Lutomirski Cc: stable@vger.kernel.org Link: https://lkml.kernel.org/r/20200225220216.219537887@linutronix.de --- arch/x86/entry/entry_32.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index 7e0560442538..39243df98100 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S @@ -1694,6 +1694,7 @@ SYM_CODE_START(int3) SYM_CODE_END(int3) SYM_CODE_START(general_protection) + ASM_CLAC pushl $do_general_protection jmp common_exception SYM_CODE_END(general_protection) From 55ba18d6ed37a28cf8b8ca79e9aef4cf98183bb7 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Tue, 25 Feb 2020 22:36:38 +0100 Subject: [PATCH 0655/2231] x86/mce: Disable tracing and kprobes on do_machine_check() do_machine_check() can be raised in almost any context including the most fragile ones. Prevent kprobes and tracing. Signed-off-by: Andy Lutomirski Signed-off-by: Thomas Gleixner Reviewed-by: Borislav Petkov Reviewed-by: Alexandre Chartre Reviewed-by: Andy Lutomirski Link: https://lkml.kernel.org/r/20200225220216.315548935@linutronix.de --- arch/x86/include/asm/traps.h | 3 --- arch/x86/kernel/cpu/mce/core.c | 12 ++++++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index ffa0dc8a535e..e1c660b9e8e6 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -88,9 +88,6 @@ dotraplinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *regs, long error_code); dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code); dotraplinkage void do_alignment_check(struct pt_regs *regs, long error_code); -#ifdef CONFIG_X86_MCE -dotraplinkage void do_machine_check(struct pt_regs *regs, long error_code); -#endif dotraplinkage void do_simd_coprocessor_error(struct pt_regs *regs, long error_code); #ifdef CONFIG_X86_32 dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code); diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 2c4f949611e4..32ecc5969d59 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -1213,8 +1213,14 @@ static void __mc_scan_banks(struct mce *m, struct mce *final, * On Intel systems this is entered on all CPUs in parallel through * MCE broadcast. However some CPUs might be broken beyond repair, * so be always careful when synchronizing with others. + * + * Tracing and kprobes are disabled: if we interrupted a kernel context + * with IF=1, we need to minimize stack usage. There are also recursion + * issues: if the machine check was due to a failure of the memory + * backing the user stack, tracing that reads the user stack will cause + * potentially infinite recursion. */ -void do_machine_check(struct pt_regs *regs, long error_code) +void notrace do_machine_check(struct pt_regs *regs, long error_code) { DECLARE_BITMAP(valid_banks, MAX_NR_BANKS); DECLARE_BITMAP(toclear, MAX_NR_BANKS); @@ -1360,6 +1366,7 @@ out_ist: ist_exit(regs); } EXPORT_SYMBOL_GPL(do_machine_check); +NOKPROBE_SYMBOL(do_machine_check); #ifndef CONFIG_MEMORY_FAILURE int memory_failure(unsigned long pfn, int flags) @@ -1892,10 +1899,11 @@ static void unexpected_machine_check(struct pt_regs *regs, long error_code) void (*machine_check_vector)(struct pt_regs *, long error_code) = unexpected_machine_check; -dotraplinkage void do_mce(struct pt_regs *regs, long error_code) +dotraplinkage notrace void do_mce(struct pt_regs *regs, long error_code) { machine_check_vector(regs, error_code); } +NOKPROBE_SYMBOL(do_mce); /* * Called for each booted CPU to set up machine checks. From 840371bea19e85f30d19909171248cf8c5845fd7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Feb 2020 22:36:39 +0100 Subject: [PATCH 0656/2231] x86/entry/32: Force MCE through do_mce() Remove the pointless difference between 32 and 64 bit to make further unifications simpler. Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Reviewed-by: Alexandre Chartre Reviewed-by: Andy Lutomirski Link: https://lkml.kernel.org/r/20200225220216.428188397@linutronix.de --- arch/x86/entry/entry_32.S | 2 +- arch/x86/include/asm/mce.h | 3 --- arch/x86/kernel/cpu/mce/internal.h | 3 +++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index 39243df98100..a8b44384699e 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S @@ -1365,7 +1365,7 @@ SYM_CODE_END(divide_error) SYM_CODE_START(machine_check) ASM_CLAC pushl $0 - pushl machine_check_vector + pushl $do_mce jmp common_exception SYM_CODE_END(machine_check) #endif diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index 4359b955e0b7..a7e0b9d370a4 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -238,9 +238,6 @@ extern void mce_disable_bank(int bank); /* * Exception handler */ - -/* Call the installed machine check handler for this CPU setup. */ -extern void (*machine_check_vector)(struct pt_regs *, long error_code); void do_machine_check(struct pt_regs *, long); /* diff --git a/arch/x86/kernel/cpu/mce/internal.h b/arch/x86/kernel/cpu/mce/internal.h index b785c0d0b590..c1cda0b7aa89 100644 --- a/arch/x86/kernel/cpu/mce/internal.h +++ b/arch/x86/kernel/cpu/mce/internal.h @@ -8,6 +8,9 @@ #include #include +/* Pointer to the installed machine check handler for this CPU setup. */ +extern void (*machine_check_vector)(struct pt_regs *, long error_code); + enum severity_level { MCE_NO_SEVERITY, MCE_DEFERRED_SEVERITY, From e039dd815941e785203142261397da6ec64d20cc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Feb 2020 22:36:40 +0100 Subject: [PATCH 0657/2231] x86/traps: Remove pointless irq enable from do_spurious_interrupt_bug() That function returns immediately after conditionally reenabling interrupts which is more than pointless and requires the ASM code to disable interrupts again. Signed-off-by: Thomas Gleixner Reviewed-by: Sean Christopherson Reviewed-by: Alexandre Chartre Reviewed-by: Frederic Weisbecker Reviewed-by: Andy Lutomirski Acked-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/20191023123117.871608831@linutronix.de Link: https://lkml.kernel.org/r/20200225220216.518575042@linutronix.de --- arch/x86/kernel/traps.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 6ef00eb6fbb9..474b8cb54899 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -862,7 +862,6 @@ do_simd_coprocessor_error(struct pt_regs *regs, long error_code) dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) { - cond_local_irq_enable(regs); } dotraplinkage void From d244d0e195bc12964bcf3b8eef45e715a7f203b0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Feb 2020 22:36:41 +0100 Subject: [PATCH 0658/2231] x86/traps: Document do_spurious_interrupt_bug() Add a comment which explains why this empty handler for a reserved vector exists. Requested-by: Josh Poimboeuf Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Reviewed-by: Alexandre Chartre Reviewed-by: Andy Lutomirski Link: https://lkml.kernel.org/r/20200225220216.624165786@linutronix.de --- arch/x86/kernel/traps.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 474b8cb54899..7ffb6f4dea1c 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -862,6 +862,25 @@ do_simd_coprocessor_error(struct pt_regs *regs, long error_code) dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) { + /* + * This addresses a Pentium Pro Erratum: + * + * PROBLEM: If the APIC subsystem is configured in mixed mode with + * Virtual Wire mode implemented through the local APIC, an + * interrupt vector of 0Fh (Intel reserved encoding) may be + * generated by the local APIC (Int 15). This vector may be + * generated upon receipt of a spurious interrupt (an interrupt + * which is removed before the system receives the INTA sequence) + * instead of the programmed 8259 spurious interrupt vector. + * + * IMPLICATION: The spurious interrupt vector programmed in the + * 8259 is normally handled by an operating system's spurious + * interrupt handler. However, a vector of 0Fh is unknown to some + * operating systems, which would crash if this erratum occurred. + * + * In theory this could be limited to 32bit, but the handler is not + * hurting and who knows which other CPUs suffer from this. + */ } dotraplinkage void From 3ba4f0a633ca4bfeadb24eba19cb53ebe246eabf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Feb 2020 22:36:42 +0100 Subject: [PATCH 0659/2231] x86/traps: Remove redundant declaration of do_double_fault() Signed-off-by: Thomas Gleixner Reviewed-by: Alexandre Chartre Reviewed-by: Andy Lutomirski Link: https://lkml.kernel.org/r/20200225220216.720335354@linutronix.de --- arch/x86/include/asm/traps.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index e1c660b9e8e6..c26a7e1d8a2c 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -76,13 +76,6 @@ dotraplinkage void do_coprocessor_segment_overrun(struct pt_regs *regs, long err dotraplinkage void do_invalid_TSS(struct pt_regs *regs, long error_code); dotraplinkage void do_segment_not_present(struct pt_regs *regs, long error_code); dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code); -#ifdef CONFIG_X86_64 -dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsigned long address); -asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs); -asmlinkage __visible notrace -struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s); -void __init trap_init(void); -#endif dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code); dotraplinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address); dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *regs, long error_code); @@ -94,6 +87,13 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code); #endif dotraplinkage void do_mce(struct pt_regs *regs, long error_code); +#ifdef CONFIG_X86_64 +asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs); +asmlinkage __visible notrace +struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s); +void __init trap_init(void); +#endif + static inline int get_si_code(unsigned long condition) { if (condition & DR_STEP) From 17dbedb5da184b501c5c51fc78d1071005139e48 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Feb 2020 22:36:43 +0100 Subject: [PATCH 0660/2231] x86/irq: Remove useless return value from do_IRQ() Nothing is using it. Signed-off-by: Thomas Gleixner Reviewed-by: Alexandre Chartre Reviewed-by: Andy Lutomirski Link: https://lkml.kernel.org/r/20200225220216.826870369@linutronix.de --- arch/x86/include/asm/irq.h | 2 +- arch/x86/kernel/irq.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h index a176f6165d85..72fba0eeeb30 100644 --- a/arch/x86/include/asm/irq.h +++ b/arch/x86/include/asm/irq.h @@ -36,7 +36,7 @@ extern void native_init_IRQ(void); extern void handle_irq(struct irq_desc *desc, struct pt_regs *regs); -extern __visible unsigned int do_IRQ(struct pt_regs *regs); +extern __visible void do_IRQ(struct pt_regs *regs); extern void init_ISA_irqs(void); diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 21efee32e2b1..c7965ff429c5 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -230,7 +230,7 @@ u64 arch_irq_stat(void) * SMP cross-CPU interrupts have their own specific * handlers). */ -__visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs) +__visible void __irq_entry do_IRQ(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); struct irq_desc * desc; @@ -263,7 +263,6 @@ __visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs) exiting_irq(); set_irq_regs(old_regs); - return 1; } #ifdef CONFIG_X86_LOCAL_APIC From ac3607f92f70c762e24d0ae731168f7584de51ec Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 25 Feb 2020 22:36:45 +0100 Subject: [PATCH 0661/2231] x86/entry/entry_32: Route int3 through common_exception int3 is not using the common_exception path for purely historical reasons, but there is no reason to keep it the only exception which is different. Make it use common_exception so the upcoming changes to autogenerate the entry stubs do not have to special case int3. Signed-off-by: Thomas Gleixner Reviewed-by: Frederic Weisbecker Reviewed-by: Alexandre Chartre Reviewed-by: Andy Lutomirski Link: https://lkml.kernel.org/r/20200225220217.042369808@linutronix.de --- arch/x86/entry/entry_32.S | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index a8b44384699e..0753f4879a80 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S @@ -1683,14 +1683,8 @@ SYM_CODE_END(nmi) SYM_CODE_START(int3) ASM_CLAC pushl $-1 # mark this as an int - - SAVE_ALL switch_stacks=1 - ENCODE_FRAME_POINTER - TRACE_IRQS_OFF - xorl %edx, %edx # zero error code - movl %esp, %eax # pt_regs pointer - call do_int3 - jmp ret_from_exception + pushl $do_int3 + jmp common_exception SYM_CODE_END(int3) SYM_CODE_START(general_protection) From 65c668f5faebf549db086b7a6841b6f4187b4e4f Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Tue, 25 Feb 2020 22:36:46 +0100 Subject: [PATCH 0662/2231] x86/traps: Stop using ist_enter/exit() in do_int3() #BP is not longer using IST and using ist_enter() and ist_exit() makes it harder to change ist_enter() and ist_exit()'s behavior. Instead open-code the very small amount of required logic. Signed-off-by: Andy Lutomirski Signed-off-by: Thomas Gleixner Reviewed-by: Alexandre Chartre Reviewed-by: Andy Lutomirski Link: https://lkml.kernel.org/r/20200225220217.150607679@linutronix.de --- arch/x86/kernel/traps.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 7ffb6f4dea1c..c0bc9df8634d 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -572,14 +572,20 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) return; /* - * Use ist_enter despite the fact that we don't use an IST stack. - * We can be called from a kprobe in non-CONTEXT_KERNEL kernel - * mode or even during context tracking state changes. + * Unlike any other non-IST entry, we can be called from a kprobe in + * non-CONTEXT_KERNEL kernel mode or even during context tracking + * state changes. Make sure that we wake up RCU even if we're coming + * from kernel code. * - * This means that we can't schedule. That's okay. + * This means that we can't schedule even if we came from a + * preemptible kernel context. That's okay. */ - ist_enter(regs); + if (!user_mode(regs)) { + rcu_nmi_enter(); + preempt_disable(); + } RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); + #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, SIGTRAP) == NOTIFY_STOP) @@ -600,7 +606,10 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) cond_local_irq_disable(regs); exit: - ist_exit(regs); + if (!user_mode(regs)) { + preempt_enable_no_resched(); + rcu_nmi_exit(); + } } NOKPROBE_SYMBOL(do_int3); From dc8cb9df2b863c71027c6fee8ce370775d739fc3 Mon Sep 17 00:00:00 2001 From: "Joel Fernandes (Google)" Date: Thu, 13 Feb 2020 16:38:21 -0500 Subject: [PATCH 0663/2231] doc: Add some more RCU list patterns in the kernel - Add more information about RCU list patterns taking examples from audit subsystem in the linux kernel. - Keep the current audit examples, even though the kernel has changed. - Modify inline text for better passage quality. - Fix typo in code-blocks and improve code comments. - Add text formatting (italics, bold and code) for better emphasis. Patch originally submitted at https://lore.kernel.org/patchwork/patch/1082804/ Co-developed-by: Amol Grover Signed-off-by: Amol Grover Signed-off-by: Joel Fernandes (Google) Signed-off-by: Paul E. McKenney --- Documentation/RCU/listRCU.rst | 275 ++++++++++++++++++++++++++-------- 1 file changed, 211 insertions(+), 64 deletions(-) diff --git a/Documentation/RCU/listRCU.rst b/Documentation/RCU/listRCU.rst index 7956ff33042b..55d2b30db481 100644 --- a/Documentation/RCU/listRCU.rst +++ b/Documentation/RCU/listRCU.rst @@ -4,12 +4,61 @@ Using RCU to Protect Read-Mostly Linked Lists ============================================= One of the best applications of RCU is to protect read-mostly linked lists -("struct list_head" in list.h). One big advantage of this approach +(``struct list_head`` in list.h). One big advantage of this approach is that all of the required memory barriers are included for you in the list macros. This document describes several applications of RCU, with the best fits first. -Example 1: Read-Side Action Taken Outside of Lock, No In-Place Updates + +Example 1: Read-mostly list: Deferred Destruction +------------------------------------------------- + +A widely used usecase for RCU lists in the kernel is lockless iteration over +all processes in the system. ``task_struct::tasks`` represents the list node that +links all the processes. The list can be traversed in parallel to any list +additions or removals. + +The traversal of the list is done using ``for_each_process()`` which is defined +by the 2 macros:: + + #define next_task(p) \ + list_entry_rcu((p)->tasks.next, struct task_struct, tasks) + + #define for_each_process(p) \ + for (p = &init_task ; (p = next_task(p)) != &init_task ; ) + +The code traversing the list of all processes typically looks like:: + + rcu_read_lock(); + for_each_process(p) { + /* Do something with p */ + } + rcu_read_unlock(); + +The simplified code for removing a process from a task list is:: + + void release_task(struct task_struct *p) + { + write_lock(&tasklist_lock); + list_del_rcu(&p->tasks); + write_unlock(&tasklist_lock); + call_rcu(&p->rcu, delayed_put_task_struct); + } + +When a process exits, ``release_task()`` calls ``list_del_rcu(&p->tasks)`` under +``tasklist_lock`` writer lock protection, to remove the task from the list of +all tasks. The ``tasklist_lock`` prevents concurrent list additions/removals +from corrupting the list. Readers using ``for_each_process()`` are not protected +with the ``tasklist_lock``. To prevent readers from noticing changes in the list +pointers, the ``task_struct`` object is freed only after one or more grace +periods elapse (with the help of call_rcu()). This deferring of destruction +ensures that any readers traversing the list will see valid ``p->tasks.next`` +pointers and deletion/freeing can happen in parallel with traversal of the list. +This pattern is also called an **existence lock**, since RCU pins the object in +memory until all existing readers finish. + + +Example 2: Read-Side Action Taken Outside of Lock: No In-Place Updates ---------------------------------------------------------------------- The best applications are cases where, if reader-writer locking were @@ -26,7 +75,7 @@ added or deleted, rather than being modified in place. A straightforward example of this use of RCU may be found in the system-call auditing support. For example, a reader-writer locked -implementation of audit_filter_task() might be as follows:: +implementation of ``audit_filter_task()`` might be as follows:: static enum audit_state audit_filter_task(struct task_struct *tsk) { @@ -34,7 +83,7 @@ implementation of audit_filter_task() might be as follows:: enum audit_state state; read_lock(&auditsc_lock); - /* Note: audit_netlink_sem held by caller. */ + /* Note: audit_filter_mutex held by caller. */ list_for_each_entry(e, &audit_tsklist, list) { if (audit_filter_rules(tsk, &e->rule, NULL, &state)) { read_unlock(&auditsc_lock); @@ -58,7 +107,7 @@ This means that RCU can be easily applied to the read side, as follows:: enum audit_state state; rcu_read_lock(); - /* Note: audit_netlink_sem held by caller. */ + /* Note: audit_filter_mutex held by caller. */ list_for_each_entry_rcu(e, &audit_tsklist, list) { if (audit_filter_rules(tsk, &e->rule, NULL, &state)) { rcu_read_unlock(); @@ -69,18 +118,18 @@ This means that RCU can be easily applied to the read side, as follows:: return AUDIT_BUILD_CONTEXT; } -The read_lock() and read_unlock() calls have become rcu_read_lock() +The ``read_lock()`` and ``read_unlock()`` calls have become rcu_read_lock() and rcu_read_unlock(), respectively, and the list_for_each_entry() has -become list_for_each_entry_rcu(). The _rcu() list-traversal primitives +become list_for_each_entry_rcu(). The **_rcu()** list-traversal primitives insert the read-side memory barriers that are required on DEC Alpha CPUs. -The changes to the update side are also straightforward. A reader-writer -lock might be used as follows for deletion and insertion:: +The changes to the update side are also straightforward. A reader-writer lock +might be used as follows for deletion and insertion:: static inline int audit_del_rule(struct audit_rule *rule, struct list_head *list) { - struct audit_entry *e; + struct audit_entry *e; write_lock(&auditsc_lock); list_for_each_entry(e, list, list) { @@ -113,9 +162,9 @@ Following are the RCU equivalents for these two functions:: static inline int audit_del_rule(struct audit_rule *rule, struct list_head *list) { - struct audit_entry *e; + struct audit_entry *e; - /* Do not use the _rcu iterator here, since this is the only + /* No need to use the _rcu iterator here, since this is the only * deletion routine. */ list_for_each_entry(e, list, list) { if (!audit_compare_rule(rule, &e->rule)) { @@ -139,41 +188,41 @@ Following are the RCU equivalents for these two functions:: return 0; } -Normally, the write_lock() and write_unlock() would be replaced by -a spin_lock() and a spin_unlock(), but in this case, all callers hold -audit_netlink_sem, so no additional locking is required. The auditsc_lock -can therefore be eliminated, since use of RCU eliminates the need for -writers to exclude readers. Normally, the write_lock() calls would -be converted into spin_lock() calls. +Normally, the ``write_lock()`` and ``write_unlock()`` would be replaced by a +spin_lock() and a spin_unlock(). But in this case, all callers hold +``audit_filter_mutex``, so no additional locking is required. The +``auditsc_lock`` can therefore be eliminated, since use of RCU eliminates the +need for writers to exclude readers. The list_del(), list_add(), and list_add_tail() primitives have been replaced by list_del_rcu(), list_add_rcu(), and list_add_tail_rcu(). -The _rcu() list-manipulation primitives add memory barriers that are -needed on weakly ordered CPUs (most of them!). The list_del_rcu() -primitive omits the pointer poisoning debug-assist code that would -otherwise cause concurrent readers to fail spectacularly. +The **_rcu()** list-manipulation primitives add memory barriers that are needed on +weakly ordered CPUs (most of them!). The list_del_rcu() primitive omits the +pointer poisoning debug-assist code that would otherwise cause concurrent +readers to fail spectacularly. -So, when readers can tolerate stale data and when entries are either added -or deleted, without in-place modification, it is very easy to use RCU! +So, when readers can tolerate stale data and when entries are either added or +deleted, without in-place modification, it is very easy to use RCU! -Example 2: Handling In-Place Updates + +Example 3: Handling In-Place Updates ------------------------------------ -The system-call auditing code does not update auditing rules in place. -However, if it did, reader-writer-locked code to do so might look as -follows (presumably, the field_count is only permitted to decrease, -otherwise, the added fields would need to be filled in):: +The system-call auditing code does not update auditing rules in place. However, +if it did, the reader-writer-locked code to do so might look as follows +(assuming only ``field_count`` is updated, otherwise, the added fields would +need to be filled in):: static inline int audit_upd_rule(struct audit_rule *rule, struct list_head *list, __u32 newaction, __u32 newfield_count) { - struct audit_entry *e; - struct audit_newentry *ne; + struct audit_entry *e; + struct audit_entry *ne; write_lock(&auditsc_lock); - /* Note: audit_netlink_sem held by caller. */ + /* Note: audit_filter_mutex held by caller. */ list_for_each_entry(e, list, list) { if (!audit_compare_rule(rule, &e->rule)) { e->rule.action = newaction; @@ -188,16 +237,16 @@ otherwise, the added fields would need to be filled in):: The RCU version creates a copy, updates the copy, then replaces the old entry with the newly updated entry. This sequence of actions, allowing -concurrent reads while doing a copy to perform an update, is what gives -RCU ("read-copy update") its name. The RCU code is as follows:: +concurrent reads while making a copy to perform an update, is what gives +RCU (*read-copy update*) its name. The RCU code is as follows:: static inline int audit_upd_rule(struct audit_rule *rule, struct list_head *list, __u32 newaction, __u32 newfield_count) { - struct audit_entry *e; - struct audit_newentry *ne; + struct audit_entry *e; + struct audit_entry *ne; list_for_each_entry(e, list, list) { if (!audit_compare_rule(rule, &e->rule)) { @@ -215,34 +264,45 @@ RCU ("read-copy update") its name. The RCU code is as follows:: return -EFAULT; /* No matching rule */ } -Again, this assumes that the caller holds audit_netlink_sem. Normally, -the reader-writer lock would become a spinlock in this sort of code. +Again, this assumes that the caller holds ``audit_filter_mutex``. Normally, the +writer lock would become a spinlock in this sort of code. -Example 3: Eliminating Stale Data +Another use of this pattern can be found in the openswitch driver's *connection +tracking table* code in ``ct_limit_set()``. The table holds connection tracking +entries and has a limit on the maximum entries. There is one such table +per-zone and hence one *limit* per zone. The zones are mapped to their limits +through a hashtable using an RCU-managed hlist for the hash chains. When a new +limit is set, a new limit object is allocated and ``ct_limit_set()`` is called +to replace the old limit object with the new one using list_replace_rcu(). +The old limit object is then freed after a grace period using kfree_rcu(). + + +Example 4: Eliminating Stale Data --------------------------------- -The auditing examples above tolerate stale data, as do most algorithms +The auditing example above tolerates stale data, as do most algorithms that are tracking external state. Because there is a delay from the time the external state changes before Linux becomes aware of the change, -additional RCU-induced staleness is normally not a problem. +additional RCU-induced staleness is generally not a problem. However, there are many examples where stale data cannot be tolerated. One example in the Linux kernel is the System V IPC (see the ipc_lock() -function in ipc/util.c). This code checks a "deleted" flag under a -per-entry spinlock, and, if the "deleted" flag is set, pretends that the +function in ipc/util.c). This code checks a *deleted* flag under a +per-entry spinlock, and, if the *deleted* flag is set, pretends that the entry does not exist. For this to be helpful, the search function must -return holding the per-entry spinlock, as ipc_lock() does in fact do. +return holding the per-entry lock, as ipc_lock() does in fact do. + +.. _quick_quiz: Quick Quiz: - Why does the search function need to return holding the per-entry lock for - this deleted-flag technique to be helpful? + For the deleted-flag technique to be helpful, why is it necessary + to hold the per-entry lock while returning from the search function? -:ref:`Answer to Quick Quiz ` +:ref:`Answer to Quick Quiz ` -If the system-call audit module were to ever need to reject stale data, -one way to accomplish this would be to add a "deleted" flag and a "lock" -spinlock to the audit_entry structure, and modify audit_filter_task() -as follows:: +If the system-call audit module were to ever need to reject stale data, one way +to accomplish this would be to add a ``deleted`` flag and a ``lock`` spinlock to the +audit_entry structure, and modify ``audit_filter_task()`` as follows:: static enum audit_state audit_filter_task(struct task_struct *tsk) { @@ -267,20 +327,20 @@ as follows:: } Note that this example assumes that entries are only added and deleted. -Additional mechanism is required to deal correctly with the -update-in-place performed by audit_upd_rule(). For one thing, -audit_upd_rule() would need additional memory barriers to ensure -that the list_add_rcu() was really executed before the list_del_rcu(). +Additional mechanism is required to deal correctly with the update-in-place +performed by ``audit_upd_rule()``. For one thing, ``audit_upd_rule()`` would +need additional memory barriers to ensure that the list_add_rcu() was really +executed before the list_del_rcu(). -The audit_del_rule() function would need to set the "deleted" -flag under the spinlock as follows:: +The ``audit_del_rule()`` function would need to set the ``deleted`` flag under the +spinlock as follows:: static inline int audit_del_rule(struct audit_rule *rule, struct list_head *list) { - struct audit_entry *e; + struct audit_entry *e; - /* Do not need to use the _rcu iterator here, since this + /* No need to use the _rcu iterator here, since this * is the only deletion routine. */ list_for_each_entry(e, list, list) { if (!audit_compare_rule(rule, &e->rule)) { @@ -295,6 +355,91 @@ flag under the spinlock as follows:: return -EFAULT; /* No matching rule */ } +This too assumes that the caller holds ``audit_filter_mutex``. + + +Example 5: Skipping Stale Objects +--------------------------------- + +For some usecases, reader performance can be improved by skipping stale objects +during read-side list traversal if the object in concern is pending destruction +after one or more grace periods. One such example can be found in the timerfd +subsystem. When a ``CLOCK_REALTIME`` clock is reprogrammed - for example due to +setting of the system time, then all programmed timerfds that depend on this +clock get triggered and processes waiting on them to expire are woken up in +advance of their scheduled expiry. To facilitate this, all such timers are added +to an RCU-managed ``cancel_list`` when they are setup in +``timerfd_setup_cancel()``:: + + static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) + { + spin_lock(&ctx->cancel_lock); + if ((ctx->clockid == CLOCK_REALTIME && + (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) { + if (!ctx->might_cancel) { + ctx->might_cancel = true; + spin_lock(&cancel_lock); + list_add_rcu(&ctx->clist, &cancel_list); + spin_unlock(&cancel_lock); + } + } + spin_unlock(&ctx->cancel_lock); + } + +When a timerfd is freed (fd is closed), then the ``might_cancel`` flag of the +timerfd object is cleared, the object removed from the ``cancel_list`` and +destroyed:: + + int timerfd_release(struct inode *inode, struct file *file) + { + struct timerfd_ctx *ctx = file->private_data; + + spin_lock(&ctx->cancel_lock); + if (ctx->might_cancel) { + ctx->might_cancel = false; + spin_lock(&cancel_lock); + list_del_rcu(&ctx->clist); + spin_unlock(&cancel_lock); + } + spin_unlock(&ctx->cancel_lock); + + hrtimer_cancel(&ctx->t.tmr); + kfree_rcu(ctx, rcu); + return 0; + } + +If the ``CLOCK_REALTIME`` clock is set, for example by a time server, the +hrtimer framework calls ``timerfd_clock_was_set()`` which walks the +``cancel_list`` and wakes up processes waiting on the timerfd. While iterating +the ``cancel_list``, the ``might_cancel`` flag is consulted to skip stale +objects:: + + void timerfd_clock_was_set(void) + { + struct timerfd_ctx *ctx; + unsigned long flags; + + rcu_read_lock(); + list_for_each_entry_rcu(ctx, &cancel_list, clist) { + if (!ctx->might_cancel) + continue; + spin_lock_irqsave(&ctx->wqh.lock, flags); + if (ctx->moffs != ktime_mono_to_real(0)) { + ctx->moffs = KTIME_MAX; + ctx->ticks++; + wake_up_locked_poll(&ctx->wqh, EPOLLIN); + } + spin_unlock_irqrestore(&ctx->wqh.lock, flags); + } + rcu_read_unlock(); + } + +The key point here is, because RCU-traversal of the ``cancel_list`` happens +while objects are being added and removed to the list, sometimes the traversal +can step on an object that has been removed from the list. In this example, it +is seen that it is better to skip such objects using a flag. + + Summary ------- @@ -303,19 +448,21 @@ the most amenable to use of RCU. The simplest case is where entries are either added or deleted from the data structure (or atomically modified in place), but non-atomic in-place modifications can be handled by making a copy, updating the copy, then replacing the original with the copy. -If stale data cannot be tolerated, then a "deleted" flag may be used +If stale data cannot be tolerated, then a *deleted* flag may be used in conjunction with a per-entry spinlock in order to allow the search function to reject newly deleted data. -.. _answer_quick_quiz_list: +.. _quick_quiz_answer: Answer to Quick Quiz: - Why does the search function need to return holding the per-entry - lock for this deleted-flag technique to be helpful? + For the deleted-flag technique to be helpful, why is it necessary + to hold the per-entry lock while returning from the search function? If the search function drops the per-entry lock before returning, then the caller will be processing stale data in any case. If it is really OK to be processing stale data, then you don't need a - "deleted" flag. If processing stale data really is a problem, + *deleted* flag. If processing stale data really is a problem, then you need to hold the per-entry lock across all of the code that uses the value that was returned. + +:ref:`Back to Quick Quiz ` From d18c265fbf19de7716e6e3054b752594133b0739 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 6 Jan 2020 21:07:56 +0100 Subject: [PATCH 0664/2231] doc/RCU/Design: Remove remaining HTML tags in ReST files Commit ccc9971e2147 ("docs: rcu: convert some articles from html to ReST") has converted a few of html RCU docs into ReST files, but a few of html tags which not supported on rst is remaining. This commit converts those to ReST appropriate alternatives. Reviewed-by: Madhuparna Bhowmik Signed-off-by: SeongJae Park Signed-off-by: Paul E. McKenney --- .../Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst b/Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst index 1a8b129cfc04..83ae3b79a643 100644 --- a/Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst +++ b/Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst @@ -4,7 +4,7 @@ A Tour Through TREE_RCU's Grace-Period Memory Ordering August 8, 2017 -This article was contributed by Paul E. McKenney +This article was contributed by Paul E. McKenney Introduction ============ @@ -48,7 +48,7 @@ Tree RCU Grace Period Memory Ordering Building Blocks The workhorse for RCU's grace-period memory ordering is the critical section for the ``rcu_node`` structure's -``->lock``. These critical sections use helper functions for lock +``->lock``. These critical sections use helper functions for lock acquisition, including ``raw_spin_lock_rcu_node()``, ``raw_spin_lock_irq_rcu_node()``, and ``raw_spin_lock_irqsave_rcu_node()``. Their lock-release counterparts are ``raw_spin_unlock_rcu_node()``, @@ -102,9 +102,9 @@ lock-acquisition and lock-release functions:: 23 r3 = READ_ONCE(x); 24 } 25 - 26 WARN_ON(r1 == 0 && r2 == 0 && r3 == 0); + 26 WARN_ON(r1 == 0 && r2 == 0 && r3 == 0); -The ``WARN_ON()`` is evaluated at “the end of time”, +The ``WARN_ON()`` is evaluated at "the end of time", after all changes have propagated throughout the system. Without the ``smp_mb__after_unlock_lock()`` provided by the acquisition functions, this ``WARN_ON()`` could trigger, for example From c50a871409dc664f2f89d11926d9a6b8ab7f5b07 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 6 Jan 2020 21:07:57 +0100 Subject: [PATCH 0665/2231] doc/RCU/listRCU: Fix typos in a example code snippets Signed-off-by: SeongJae Park Signed-off-by: Paul E. McKenney --- Documentation/RCU/listRCU.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/RCU/listRCU.rst b/Documentation/RCU/listRCU.rst index 55d2b30db481..e768f56e8fa3 100644 --- a/Documentation/RCU/listRCU.rst +++ b/Documentation/RCU/listRCU.rst @@ -226,7 +226,7 @@ need to be filled in):: list_for_each_entry(e, list, list) { if (!audit_compare_rule(rule, &e->rule)) { e->rule.action = newaction; - e->rule.file_count = newfield_count; + e->rule.field_count = newfield_count; write_unlock(&auditsc_lock); return 0; } @@ -255,7 +255,7 @@ RCU (*read-copy update*) its name. The RCU code is as follows:: return -ENOMEM; audit_copy_rule(&ne->rule, &e->rule); ne->rule.action = newaction; - ne->rule.file_count = newfield_count; + ne->rule.field_count = newfield_count; list_replace_rcu(&e->list, &ne->list); call_rcu(&e->rcu, audit_free_rule); return 0; From 3282b0469248ab25b3f40b95e9a3d357c9d946d5 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 6 Jan 2020 21:07:58 +0100 Subject: [PATCH 0666/2231] doc/RCU/listRCU: Update example function name listRCU.rst document gives an example with 'ipc_lock()', but the function has dropped off by commit 82061c57ce93 ("ipc: drop ipc_lock()"). Because the main logic of 'ipc_lock()' has melded in 'shm_lock()' by the commit, this commit updates the document to use 'shm_lock()' instead. Reviewed-by: Madhuparna Bhowmik Signed-off-by: SeongJae Park Signed-off-by: Paul E. McKenney --- Documentation/RCU/listRCU.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/RCU/listRCU.rst b/Documentation/RCU/listRCU.rst index e768f56e8fa3..2a643e293fb4 100644 --- a/Documentation/RCU/listRCU.rst +++ b/Documentation/RCU/listRCU.rst @@ -286,11 +286,11 @@ time the external state changes before Linux becomes aware of the change, additional RCU-induced staleness is generally not a problem. However, there are many examples where stale data cannot be tolerated. -One example in the Linux kernel is the System V IPC (see the ipc_lock() -function in ipc/util.c). This code checks a *deleted* flag under a +One example in the Linux kernel is the System V IPC (see the shm_lock() +function in ipc/shm.c). This code checks a *deleted* flag under a per-entry spinlock, and, if the *deleted* flag is set, pretends that the entry does not exist. For this to be helpful, the search function must -return holding the per-entry lock, as ipc_lock() does in fact do. +return holding the per-entry spinlock, as shm_lock() does in fact do. .. _quick_quiz: From be2895681d6d8b1eb5a052d4655dea0d7cc2d84d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 6 Jan 2020 21:07:59 +0100 Subject: [PATCH 0667/2231] doc/RCU/rcu: Use ':ref:' for links to other docs Signed-off-by: SeongJae Park Signed-off-by: Paul E. McKenney --- Documentation/RCU/rcu.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/RCU/rcu.rst b/Documentation/RCU/rcu.rst index 8dfb437dacc3..a1dd71d01862 100644 --- a/Documentation/RCU/rcu.rst +++ b/Documentation/RCU/rcu.rst @@ -11,8 +11,8 @@ must be long enough that any readers accessing the item being deleted have since dropped their references. For example, an RCU-protected deletion from a linked list would first remove the item from the list, wait for a grace period to elapse, then free the element. See the -Documentation/RCU/listRCU.rst file for more information on using RCU with -linked lists. +:ref:`Documentation/RCU/listRCU.rst ` for more information on +using RCU with linked lists. Frequently Asked Questions -------------------------- @@ -50,7 +50,7 @@ Frequently Asked Questions - If I am running on a uniprocessor kernel, which can only do one thing at a time, why should I wait for a grace period? - See the Documentation/RCU/UP.rst file for more information. + See :ref:`Documentation/RCU/UP.rst ` for more information. - How can I see where RCU is currently used in the Linux kernel? @@ -68,9 +68,9 @@ Frequently Asked Questions - Why the name "RCU"? - "RCU" stands for "read-copy update". The file Documentation/RCU/listRCU.rst - has more information on where this name came from, search for - "read-copy update" to find it. + "RCU" stands for "read-copy update". + :ref:`Documentation/RCU/listRCU.rst ` has more information on where + this name came from, search for "read-copy update" to find it. - I hear that RCU is patented? What is with that? From 6a534b299ab20bdb345d3a6df0fc557c963fc50d Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 6 Jan 2020 21:08:00 +0100 Subject: [PATCH 0668/2231] doc/RCU/rcu: Use absolute paths for non-rst files Signed-off-by: SeongJae Park Signed-off-by: Paul E. McKenney --- Documentation/RCU/rcu.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/RCU/rcu.rst b/Documentation/RCU/rcu.rst index a1dd71d01862..2a830c51477e 100644 --- a/Documentation/RCU/rcu.rst +++ b/Documentation/RCU/rcu.rst @@ -75,7 +75,7 @@ Frequently Asked Questions - I hear that RCU is patented? What is with that? Yes, it is. There are several known patents related to RCU, - search for the string "Patent" in RTFP.txt to find them. + search for the string "Patent" in Documentation/RCU/RTFP.txt to find them. Of these, one was allowed to lapse by the assignee, and the others have been contributed to the Linux kernel under GPL. There are now also LGPL implementations of user-level RCU @@ -88,5 +88,5 @@ Frequently Asked Questions - Where can I find more information on RCU? - See the RTFP.txt file in this directory. + See the Documentation/RCU/RTFP.txt file. Or point your browser at (http://www.rdrop.com/users/paulmck/RCU/). From 06a649b314b3e579cfe1c50112c30e3127a36dba Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 6 Jan 2020 21:08:01 +0100 Subject: [PATCH 0669/2231] doc/RCU/rcu: Use https instead of http if possible Signed-off-by: SeongJae Park Signed-off-by: Paul E. McKenney --- Documentation/RCU/rcu.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/RCU/rcu.rst b/Documentation/RCU/rcu.rst index 2a830c51477e..0e03c6ef3147 100644 --- a/Documentation/RCU/rcu.rst +++ b/Documentation/RCU/rcu.rst @@ -79,7 +79,7 @@ Frequently Asked Questions Of these, one was allowed to lapse by the assignee, and the others have been contributed to the Linux kernel under GPL. There are now also LGPL implementations of user-level RCU - available (http://liburcu.org/). + available (https://liburcu.org/). - I hear that RCU needs work in order to support realtime kernels? From 9671f30ee25151f680d2f4345b4e4c67bed6559c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 21 Jan 2020 11:50:05 -0800 Subject: [PATCH 0670/2231] doc: Add rcutorture scripting to torture.txt For testing mainline, the kvm.sh rcutorture script is the preferred approach to testing. This commit therefore adds it to the torture.txt documentation. Signed-off-by: Paul E. McKenney --- Documentation/RCU/torture.txt | 147 ++++++++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 7 deletions(-) diff --git a/Documentation/RCU/torture.txt b/Documentation/RCU/torture.txt index a41a0384d20c..af712a3c5b6a 100644 --- a/Documentation/RCU/torture.txt +++ b/Documentation/RCU/torture.txt @@ -124,9 +124,14 @@ using a dynamically allocated srcu_struct (hence "srcud-" rather than debugging. The final "T" entry contains the totals of the counters. -USAGE +USAGE ON SPECIFIC KERNEL BUILDS -The following script may be used to torture RCU: +It is sometimes desirable to torture RCU on a specific kernel build, +for example, when preparing to put that kernel build into production. +In that case, the kernel should be built with CONFIG_RCU_TORTURE_TEST=m +so that the test can be started using modprobe and terminated using rmmod. + +For example, the following script may be used to torture RCU: #!/bin/sh @@ -142,8 +147,136 @@ checked for such errors. The "rmmod" command forces a "SUCCESS", two are self-explanatory, while the last indicates that while there were no RCU failures, CPU-hotplug problems were detected. -However, the tools/testing/selftests/rcutorture/bin/kvm.sh script -provides better automation, including automatic failure analysis. -It assumes a qemu/kvm-enabled platform, and runs guest OSes out of initrd. -See tools/testing/selftests/rcutorture/doc/initrd.txt for instructions -on setting up such an initrd. + +USAGE ON MAINLINE KERNELS + +When using rcutorture to test changes to RCU itself, it is often +necessary to build a number of kernels in order to test that change +across a broad range of combinations of the relevant Kconfig options +and of the relevant kernel boot parameters. In this situation, use +of modprobe and rmmod can be quite time-consuming and error-prone. + +Therefore, the tools/testing/selftests/rcutorture/bin/kvm.sh +script is available for mainline testing for x86, arm64, and +powerpc. By default, it will run the series of tests specified by +tools/testing/selftests/rcutorture/configs/rcu/CFLIST, with each test +running for 30 minutes within a guest OS using a minimal userspace +supplied by an automatically generated initrd. After the tests are +complete, the resulting build products and console output are analyzed +for errors and the results of the runs are summarized. + +On larger systems, rcutorture testing can be accelerated by passing the +--cpus argument to kvm.sh. For example, on a 64-CPU system, "--cpus 43" +would use up to 43 CPUs to run tests concurrently, which as of v5.4 would +complete all the scenarios in two batches, reducing the time to complete +from about eight hours to about one hour (not counting the time to build +the sixteen kernels). The "--dryrun sched" argument will not run tests, +but rather tell you how the tests would be scheduled into batches. This +can be useful when working out how many CPUs to specify in the --cpus +argument. + +Not all changes require that all scenarios be run. For example, a change +to Tree SRCU might run only the SRCU-N and SRCU-P scenarios using the +--configs argument to kvm.sh as follows: "--configs 'SRCU-N SRCU-P'". +Large systems can run multiple copies of of the full set of scenarios, +for example, a system with 448 hardware threads can run five instances +of the full set concurrently. To make this happen: + + kvm.sh --cpus 448 --configs '5*CFLIST' + +Alternatively, such a system can run 56 concurrent instances of a single +eight-CPU scenario: + + kvm.sh --cpus 448 --configs '56*TREE04' + +Or 28 concurrent instances of each of two eight-CPU scenarios: + + kvm.sh --cpus 448 --configs '28*TREE03 28*TREE04' + +Of course, each concurrent instance will use memory, which can be +limited using the --memory argument, which defaults to 512M. Small +values for memory may require disabling the callback-flooding tests +using the --bootargs parameter discussed below. + +Sometimes additional debugging is useful, and in such cases the --kconfig +parameter to kvm.sh may be used, for example, "--kconfig 'CONFIG_KASAN=y'". + +Kernel boot arguments can also be supplied, for example, to control +rcutorture's module parameters. For example, to test a change to RCU's +CPU stall-warning code, use "--bootargs 'rcutorture.stall_cpu=30'". +This will of course result in the scripting reporting a failure, namely +the resuling RCU CPU stall warning. As noted above, reducing memory may +require disabling rcutorture's callback-flooding tests: + + kvm.sh --cpus 448 --configs '56*TREE04' --memory 128M \ + --bootargs 'rcutorture.fwd_progress=0' + +Sometimes all that is needed is a full set of kernel builds. This is +what the --buildonly argument does. + +Finally, the --trust-make argument allows each kernel build to reuse what +it can from the previous kernel build. + +There are additional more arcane arguments that are documented in the +source code of the kvm.sh script. + +If a run contains failures, the number of buildtime and runtime failures +is listed at the end of the kvm.sh output, which you really should redirect +to a file. The build products and console output of each run is kept in +tools/testing/selftests/rcutorture/res in timestamped directories. A +given directory can be supplied to kvm-find-errors.sh in order to have +it cycle you through summaries of errors and full error logs. For example: + + tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh \ + tools/testing/selftests/rcutorture/res/2020.01.20-15.54.23 + +However, it is often more convenient to access the files directly. +Files pertaining to all scenarios in a run reside in the top-level +directory (2020.01.20-15.54.23 in the example above), while per-scenario +files reside in a subdirectory named after the scenario (for example, +"TREE04"). If a given scenario ran more than once (as in "--configs +'56*TREE04'" above), the directories corresponding to the second and +subsequent runs of that scenario include a sequence number, for example, +"TREE04.2", "TREE04.3", and so on. + +The most frequently used file in the top-level directory is testid.txt. +If the test ran in a git repository, then this file contains the commit +that was tested and any uncommitted changes in diff format. + +The most frequently used files in each per-scenario-run directory are: + +.config: This file contains the Kconfig options. + +Make.out: This contains build output for a specific scenario. + +console.log: This contains the console output for a specific scenario. + This file may be examined once the kernel has booted, but + it might not exist if the build failed. + +vmlinux: This contains the kernel, which can be useful with tools like + objdump and gdb. + +A number of additional files are available, but are less frequently used. +Many are intended for debugging of rcutorture itself or of its scripting. + +As of v5.4, a successful run with the default set of scenarios produces +the following summary at the end of the run on a 12-CPU system: + +SRCU-N ------- 804233 GPs (148.932/s) [srcu: g10008272 f0x0 ] +SRCU-P ------- 202320 GPs (37.4667/s) [srcud: g1809476 f0x0 ] +SRCU-t ------- 1122086 GPs (207.794/s) [srcu: g0 f0x0 ] +SRCU-u ------- 1111285 GPs (205.794/s) [srcud: g1 f0x0 ] +TASKS01 ------- 19666 GPs (3.64185/s) [tasks: g0 f0x0 ] +TASKS02 ------- 20541 GPs (3.80389/s) [tasks: g0 f0x0 ] +TASKS03 ------- 19416 GPs (3.59556/s) [tasks: g0 f0x0 ] +TINY01 ------- 836134 GPs (154.84/s) [rcu: g0 f0x0 ] n_max_cbs: 34198 +TINY02 ------- 850371 GPs (157.476/s) [rcu: g0 f0x0 ] n_max_cbs: 2631 +TREE01 ------- 162625 GPs (30.1157/s) [rcu: g1124169 f0x0 ] +TREE02 ------- 333003 GPs (61.6672/s) [rcu: g2647753 f0x0 ] n_max_cbs: 35844 +TREE03 ------- 306623 GPs (56.782/s) [rcu: g2975325 f0x0 ] n_max_cbs: 1496497 +CPU count limited from 16 to 12 +TREE04 ------- 246149 GPs (45.5831/s) [rcu: g1695737 f0x0 ] n_max_cbs: 434961 +TREE05 ------- 314603 GPs (58.2598/s) [rcu: g2257741 f0x2 ] n_max_cbs: 193997 +TREE07 ------- 167347 GPs (30.9902/s) [rcu: g1079021 f0x0 ] n_max_cbs: 478732 +CPU count limited from 16 to 12 +TREE09 ------- 752238 GPs (139.303/s) [rcu: g13075057 f0x0 ] n_max_cbs: 99011 From 8149b5cbfa1540cd7542fd4e790a2874afbc5001 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Fri, 31 Jan 2020 21:52:37 +0100 Subject: [PATCH 0671/2231] Documentation/memory-barriers: Fix typos Signed-off-by: SeongJae Park Signed-off-by: Paul E. McKenney --- Documentation/memory-barriers.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index 7146da061693..e1c355e84edd 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -185,7 +185,7 @@ As a further example, consider this sequence of events: =============== =============== { A == 1, B == 2, C == 3, P == &A, Q == &C } B = 4; Q = P; - P = &B D = *Q; + P = &B; D = *Q; There is an obvious data dependency here, as the value loaded into D depends on the address retrieved from P by CPU 2. At the end of the sequence, any of the @@ -569,7 +569,7 @@ following sequence of events: { A == 1, B == 2, C == 3, P == &A, Q == &C } B = 4; - WRITE_ONCE(P, &B) + WRITE_ONCE(P, &B); Q = READ_ONCE(P); D = *Q; @@ -1721,7 +1721,7 @@ of optimizations: and WRITE_ONCE() are more selective: With READ_ONCE() and WRITE_ONCE(), the compiler need only forget the contents of the indicated memory locations, while with barrier() the compiler must - discard the value of all memory locations that it has currented + discard the value of all memory locations that it has currently cached in any machine registers. Of course, the compiler must also respect the order in which the READ_ONCE()s and WRITE_ONCE()s occur, though the CPU of course need not do so. @@ -1833,7 +1833,7 @@ Aside: In the case of data dependencies, the compiler would be expected to issue the loads in the correct order (eg. `a[b]` would have to load the value of b before loading a[b]), however there is no guarantee in the C specification that the compiler may not speculate the value of b -(eg. is equal to 1) and load a before b (eg. tmp = a[1]; if (b != 1) +(eg. is equal to 1) and load a[b] before b (eg. tmp = a[1]; if (b != 1) tmp = a[b]; ). There is also the problem of a compiler reloading b after having loaded a[b], thus having a newer copy of b than a[b]. A consensus has not yet been reached about these problems, however the READ_ONCE() From 9cd5e5ab552546e79943ccde2b5ec0e4a090af1f Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 13 Jan 2020 15:48:50 +0100 Subject: [PATCH 0672/2231] media: MAINTAINERS: Add dt-binding files for Venus Adds a path to devicetree dt-binding files. Acked-by: Hans Verkuil Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index fe3ab10354c2..9d45b59f33ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13899,6 +13899,7 @@ L: linux-arm-msm@vger.kernel.org T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/platform/qcom/venus/ +F: Documentation/devicetree/bindings/media/*venus* QUALCOMM WCN36XX WIRELESS DRIVER M: Kalle Valo From c50cc6dc6c48300af63a6fbc71b647053c15fc80 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 9 Dec 2019 20:16:52 +0100 Subject: [PATCH 0673/2231] media: venus: hfi_parser: Ignore HEVC encoding for V1 Some older MSM8916 Venus firmware versions also seem to indicate support for encoding HEVC, even though they really can't. This will lead to errors later because hfi_session_init() fails in this case. HEVC is already ignored for "dec_codecs", so add the same for "enc_codecs" to make these old firmware versions work correctly. Signed-off-by: Stephan Gerhold Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_parser.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 2293d936e49c..7f515a4b9bd1 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -181,6 +181,7 @@ static void parse_codecs(struct venus_core *core, void *data) if (IS_V1(core)) { core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC; core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; + core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC; } } From 2e6fc23eed9b30f0df0331df1d8e3d2f39b0ed59 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 15 Nov 2019 15:30:44 +0100 Subject: [PATCH 0674/2231] media: venus: firmware: Use %pR to print IO resource Replace explicit use of members of struct resource by %pR to print the resource. Signed-off-by: Andy Shevchenko Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/firmware.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index d3d1748a7ef6..de6812fb55f4 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -100,8 +100,7 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, mem_va = memremap(r.start, *mem_size, MEMREMAP_WC); if (!mem_va) { - dev_err(dev, "unable to map memory region: %pa+%zx\n", - &r.start, *mem_size); + dev_err(dev, "unable to map memory region: %pR\n", &r); ret = -ENOMEM; goto err_release_fw; } From 4b997dbbc999692e9913f8134343199e3ba1499d Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 12 Dec 2019 10:35:21 +0100 Subject: [PATCH 0675/2231] media: venus: venc: blacklist two encoder properties Those two properties are not implemented for Venus v4 (sdm845), thus don't set them to firmware. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/hfi_cmds.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 4f645076abfb..c67e412f8201 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1207,6 +1207,8 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: + case HFI_PROPERTY_PARAM_VENC_SESSION_QP: + case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE: /* not implemented on Venus 4xx */ return -ENOTSUPP; default: From fd1ee315dcd4a0f913a74939eb88f6d9b0bd9250 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 13 Feb 2020 10:55:14 +0100 Subject: [PATCH 0676/2231] media: venus: cache vb payload to be used by clock scaling Instead of iterate over previously queued buffers in clock scaling code do cache the payload in instance context structure for later use when calculating new clock rate. This will avoid to use spin locks during buffer list iteration in clock_scaling. This fixes following kernel Oops: Unable to handle kernel paging request at virtual address deacfffffffffd6c Mem abort info: ESR = 0x96000004 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 Data abort info: ISV = 0, ISS = 0x00000004 CM = 0, WnR = 0 [deacfffffffffd6c] address between user and kernel address ranges Internal error: Oops: 96000004 [#1] PREEMPT SMP CPU: 7 PID: 5763 Comm: V4L2DecoderThre Tainted: G S W 5.4.11 #8 pstate: 20400009 (nzCv daif +PAN -UAO) pc : load_scale_v4+0x4c/0x2bc [venus_core] lr : session_process_buf+0x18c/0x1c0 [venus_core] sp : ffffffc01376b8d0 x29: ffffffc01376b8d0 x28: ffffff80cf1b0220 x27: ffffffc01376bba0 x26: ffffffd8f562b2d8 x25: ffffff80cf1b0220 x24: 0000000000000005 x23: ffffffd8f5620d98 x22: ffffff80ca01c800 x21: ffffff80cf1b0000 x20: ffffff8149490080 x19: ffffff8174b2c010 x18: 0000000000000000 x17: 0000000000000000 x16: ffffffd96ee3a0dc x15: 0000000000000026 x14: 0000000000000026 x13: 00000000000055ac x12: 0000000000000001 x11: deacfffffffffd6c x10: dead000000000100 x9 : ffffff80ca01cf28 x8 : 0000000000000026 x7 : 0000000000000000 x6 : ffffff80cdd899c0 x5 : ffffff80cdd899c0 x4 : 0000000000000008 x3 : ffffff80ca01cf28 x2 : ffffff80ca01cf28 x1 : ffffff80d47ffc00 x0 : ffffff80cf1b0000 Call trace: load_scale_v4+0x4c/0x2bc [venus_core] session_process_buf+0x18c/0x1c0 [venus_core] venus_helper_vb2_buf_queue+0x7c/0xf0 [venus_core] __enqueue_in_driver+0xe4/0xfc [videobuf2_common] vb2_core_qbuf+0x15c/0x338 [videobuf2_common] vb2_qbuf+0x78/0xb8 [videobuf2_v4l2] v4l2_m2m_qbuf+0x80/0xf8 [v4l2_mem2mem] v4l2_m2m_ioctl_qbuf+0x2c/0x38 [v4l2_mem2mem] v4l_qbuf+0x48/0x58 __video_do_ioctl+0x2b0/0x39c video_usercopy+0x394/0x710 video_ioctl2+0x38/0x48 v4l2_ioctl+0x6c/0x80 do_video_ioctl+0xb00/0x2874 v4l2_compat_ioctl32+0x5c/0xcc __se_compat_sys_ioctl+0x100/0x2074 __arm64_compat_sys_ioctl+0x20/0x2c el0_svc_common+0xa4/0x154 el0_svc_compat_handler+0x2c/0x38 el0_svc_compat+0x8/0x10 Code: eb0a013f 54000200 aa1f03e8 d10e514b (b940016c) ---[ end trace e11304b46552e0b9 ]--- Fixes: c0e284ccfeda ("media: venus: Update clock scaling") Cc: stable@vger.kernel.org # v5.5+ Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/helpers.c | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 11585fb3cae3..2f661af7f873 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -344,6 +344,7 @@ struct venus_inst { unsigned int subscriptions; int buf_count; struct venus_ts_metadata tss[VIDEO_MAX_FRAME]; + unsigned long payloads[VIDEO_MAX_FRAME]; u64 fps; struct v4l2_fract timeperframe; const struct venus_format *fmt_out; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index a172f1ac0b35..32f8fb8d7f33 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -544,18 +544,13 @@ static int scale_clocks_v4(struct venus_inst *inst) struct venus_core *core = inst->core; const struct freq_tbl *table = core->res->freq_tbl; unsigned int num_rows = core->res->freq_tbl_size; - struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; struct device *dev = core->dev; unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; unsigned long filled_len = 0; - struct venus_buffer *buf, *n; - struct vb2_buffer *vb; int i, ret; - v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { - vb = &buf->vb.vb2_buf; - filled_len = max(filled_len, vb2_get_plane_payload(vb, 0)); - } + for (i = 0; i < inst->num_input_bufs; i++) + filled_len = max(filled_len, inst->payloads[i]); if (inst->session_type == VIDC_SESSION_TYPE_DEC && !filled_len) return 0; @@ -1289,6 +1284,15 @@ int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb) } EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_prepare); +static void cache_payload(struct venus_inst *inst, struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + unsigned int idx = vbuf->vb2_buf.index; + + if (vbuf->vb2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->payloads[idx] = vb2_get_plane_payload(vb, 0); +} + void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -1300,6 +1304,8 @@ void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(m2m_ctx, vbuf); + cache_payload(inst, vb); + if (inst->session_type == VIDC_SESSION_TYPE_ENC && !(inst->streamon_out && inst->streamon_cap)) goto unlock; From 7482a983dea3b8dc7590096f2afd532af6a5f2d2 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Thu, 5 Dec 2019 16:29:48 +0100 Subject: [PATCH 0677/2231] media: venus: redesign clocks and pm domains control Redesign core (vcodec) clock control to give the venus core more freedom to control them in order to make possible to use core selection feature on Venus IP v4. Move all clock and pmdomain functions in separate file and abstract power control with common operations per Venus IP version. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/Makefile | 2 +- drivers/media/platform/qcom/venus/core.c | 95 +-- drivers/media/platform/qcom/venus/core.h | 27 +- drivers/media/platform/qcom/venus/helpers.c | 316 +------ drivers/media/platform/qcom/venus/helpers.h | 3 - .../media/platform/qcom/venus/pm_helpers.c | 800 ++++++++++++++++++ .../media/platform/qcom/venus/pm_helpers.h | 39 + drivers/media/platform/qcom/venus/vdec.c | 71 +- drivers/media/platform/qcom/venus/venc.c | 67 +- 9 files changed, 934 insertions(+), 486 deletions(-) create mode 100644 drivers/media/platform/qcom/venus/pm_helpers.c create mode 100644 drivers/media/platform/qcom/venus/pm_helpers.h diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index b44b11b03e12..64af0bc1edae 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -3,7 +3,7 @@ venus-core-objs += core.o helpers.o firmware.o \ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ - hfi_parser.o + hfi_parser.o pm_helpers.o venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 07312a2fab24..a20ab00a8068 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -3,7 +3,6 @@ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. */ -#include #include #include #include @@ -19,9 +18,8 @@ #include #include "core.h" -#include "vdec.h" -#include "venc.h" #include "firmware.h" +#include "pm_helpers.h" static void venus_event_notify(struct venus_core *core, u32 event) { @@ -100,50 +98,6 @@ static void venus_sys_error_handler(struct work_struct *work) mutex_unlock(&core->lock); } -static int venus_clks_get(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - struct device *dev = core->dev; - unsigned int i; - - for (i = 0; i < res->clks_num; i++) { - core->clks[i] = devm_clk_get(dev, res->clks[i]); - if (IS_ERR(core->clks[i])) - return PTR_ERR(core->clks[i]); - } - - return 0; -} - -static int venus_clks_enable(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - unsigned int i; - int ret; - - for (i = 0; i < res->clks_num; i++) { - ret = clk_prepare_enable(core->clks[i]); - if (ret) - goto err; - } - - return 0; -err: - while (i--) - clk_disable_unprepare(core->clks[i]); - - return ret; -} - -static void venus_clks_disable(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - unsigned int i = res->clks_num; - - while (i--) - clk_disable_unprepare(core->clks[i]); -} - static u32 to_v4l2_codec_type(u32 codec) { switch (codec) { @@ -256,9 +210,15 @@ static int venus_probe(struct platform_device *pdev) if (!core->res) return -ENODEV; - ret = venus_clks_get(core); - if (ret) - return ret; + core->pm_ops = venus_pm_get(core->res->hfi_version); + if (!core->pm_ops) + return -ENODEV; + + if (core->pm_ops->core_get) { + ret = core->pm_ops->core_get(dev); + if (ret) + return ret; + } ret = dma_set_mask_and_coherent(dev, core->res->dma_mask); if (ret) @@ -350,6 +310,7 @@ err_runtime_disable: static int venus_remove(struct platform_device *pdev) { struct venus_core *core = platform_get_drvdata(pdev); + const struct venus_pm_ops *pm_ops = core->pm_ops; struct device *dev = core->dev; int ret; @@ -368,6 +329,9 @@ static int venus_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); pm_runtime_disable(dev); + if (pm_ops->core_put) + pm_ops->core_put(dev); + icc_put(core->video_path); icc_put(core->cpucfg_path); @@ -379,11 +343,15 @@ static int venus_remove(struct platform_device *pdev) static __maybe_unused int venus_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + const struct venus_pm_ops *pm_ops = core->pm_ops; int ret; ret = hfi_core_suspend(core); + if (ret) + return ret; - venus_clks_disable(core); + if (pm_ops->core_power) + ret = pm_ops->core_power(dev, POWER_OFF); return ret; } @@ -391,21 +359,16 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev) static __maybe_unused int venus_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + const struct venus_pm_ops *pm_ops = core->pm_ops; int ret; - ret = venus_clks_enable(core); - if (ret) - return ret; + if (pm_ops->core_power) { + ret = pm_ops->core_power(dev, POWER_ON); + if (ret) + return ret; + } - ret = hfi_core_resume(core, false); - if (ret) - goto err_clks_disable; - - return 0; - -err_clks_disable: - venus_clks_disable(core); - return ret; + return hfi_core_resume(core, false); } static const struct dev_pm_ops venus_pm_ops = { @@ -463,6 +426,9 @@ static const struct venus_resources msm8996_res = { .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), .clks = {"core", "iface", "bus", "mbus" }, .clks_num = 4, + .vcodec0_clks = { "core" }, + .vcodec1_clks = { "core" }, + .vcodec_clks_num = 1, .max_load = 2563200, .hfi_version = HFI_VERSION_3XX, .vmem_id = VIDC_RESOURCE_NONE, @@ -517,6 +483,9 @@ static const struct venus_resources sdm845_res = { .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, + .vcodec0_clks = { "core", "bus" }, + .vcodec1_clks = { "core", "bus" }, + .vcodec_clks_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, .vmem_id = VIDC_RESOURCE_NONE, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 2f661af7f873..e587fd7e13c3 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -14,7 +14,9 @@ #include "hfi.h" -#define VIDC_CLKS_NUM_MAX 4 +#define VIDC_CLKS_NUM_MAX 4 +#define VIDC_VCODEC_CLKS_NUM_MAX 2 +#define VIDC_PMDOMAINS_NUM_MAX 3 struct freq_tbl { unsigned int load; @@ -55,6 +57,12 @@ struct venus_resources { unsigned int codec_freq_data_size; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; + const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + unsigned int vcodec_clks_num; + const char * const vcodec_pmdomains[VIDC_PMDOMAINS_NUM_MAX]; + unsigned int vcodec_pmdomains_num; + unsigned int vcodec_num; enum hfi_version hfi_version; u32 max_load; unsigned int vmem_id; @@ -100,10 +108,10 @@ struct venus_caps { * @base: IO memory base address * @irq: Venus irq * @clks: an array of struct clk pointers - * @core0_clk: a struct clk pointer for core0 - * @core1_clk: a struct clk pointer for core1 - * @core0_bus_clk: a struct clk pointer for core0 bus clock - * @core1_bus_clk: a struct clk pointer for core1 bus clock + * @vcodec0_clks: an array of vcodec0 struct clk pointers + * @vcodec1_clks: an array of vcodec1 struct clk pointers + * @pd_dl_venus: pmdomain device-link for venus domain + * @pmdomains: an array of pmdomains struct device pointers * @vdev_dec: a reference to video device structure for decoder instances * @vdev_enc: a reference to video device structure for encoder instances * @v4l2_dev: a holder for v4l2 device structure @@ -132,12 +140,12 @@ struct venus_core { void __iomem *base; int irq; struct clk *clks[VIDC_CLKS_NUM_MAX]; - struct clk *core0_clk; - struct clk *core1_clk; - struct clk *core0_bus_clk; - struct clk *core1_bus_clk; + struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; struct icc_path *cpucfg_path; + struct device_link *pd_dl_venus; + struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX]; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; @@ -159,6 +167,7 @@ struct venus_core { unsigned int error; bool sys_error; const struct hfi_core_ops *core_ops; + const struct venus_pm_ops *pm_ops; unsigned long enc_codecs; unsigned long dec_codecs; unsigned int max_sessions_supported; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 32f8fb8d7f33..aaf88b184efd 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -3,12 +3,8 @@ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. */ -#include -#include -#include #include #include -#include #include #include #include @@ -17,7 +13,7 @@ #include "core.h" #include "helpers.h" #include "hfi_helper.h" -#include "hfi_venus_io.h" +#include "pm_helpers.h" struct intbuf { struct list_head list; @@ -360,261 +356,6 @@ err: } EXPORT_SYMBOL_GPL(venus_helper_intbufs_realloc); -static u32 load_per_instance(struct venus_inst *inst) -{ - u32 mbs; - - if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP)) - return 0; - - mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16); - - return mbs * inst->fps; -} - -static u32 load_per_type(struct venus_core *core, u32 session_type) -{ - struct venus_inst *inst = NULL; - u32 mbs_per_sec = 0; - - mutex_lock(&core->lock); - list_for_each_entry(inst, &core->instances, list) { - if (inst->session_type != session_type) - continue; - - mbs_per_sec += load_per_instance(inst); - } - mutex_unlock(&core->lock); - - return mbs_per_sec; -} - -static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak) -{ - const struct venus_resources *res = inst->core->res; - const struct bw_tbl *bw_tbl; - unsigned int num_rows, i; - - *avg = 0; - *peak = 0; - - if (mbs == 0) - return; - - if (inst->session_type == VIDC_SESSION_TYPE_ENC) { - num_rows = res->bw_tbl_enc_size; - bw_tbl = res->bw_tbl_enc; - } else if (inst->session_type == VIDC_SESSION_TYPE_DEC) { - num_rows = res->bw_tbl_dec_size; - bw_tbl = res->bw_tbl_dec; - } else { - return; - } - - if (!bw_tbl || num_rows == 0) - return; - - for (i = 0; i < num_rows; i++) { - if (mbs > bw_tbl[i].mbs_per_sec) - break; - - if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) { - *avg = bw_tbl[i].avg_10bit; - *peak = bw_tbl[i].peak_10bit; - } else { - *avg = bw_tbl[i].avg; - *peak = bw_tbl[i].peak; - } - } -} - -static int load_scale_bw(struct venus_core *core) -{ - struct venus_inst *inst = NULL; - u32 mbs_per_sec, avg, peak, total_avg = 0, total_peak = 0; - - mutex_lock(&core->lock); - list_for_each_entry(inst, &core->instances, list) { - mbs_per_sec = load_per_instance(inst); - mbs_to_bw(inst, mbs_per_sec, &avg, &peak); - total_avg += avg; - total_peak += peak; - } - mutex_unlock(&core->lock); - - dev_dbg(core->dev, "total: avg_bw: %u, peak_bw: %u\n", - total_avg, total_peak); - - return icc_set_bw(core->video_path, total_avg, total_peak); -} - -static int set_clk_freq(struct venus_core *core, unsigned long freq) -{ - struct clk *clk = core->clks[0]; - int ret; - - ret = clk_set_rate(clk, freq); - if (ret) - return ret; - - ret = clk_set_rate(core->core0_clk, freq); - if (ret) - return ret; - - ret = clk_set_rate(core->core1_clk, freq); - if (ret) - return ret; - - return 0; -} - -static int scale_clocks(struct venus_inst *inst) -{ - struct venus_core *core = inst->core; - const struct freq_tbl *table = core->res->freq_tbl; - unsigned int num_rows = core->res->freq_tbl_size; - unsigned long freq = table[0].freq; - struct device *dev = core->dev; - u32 mbs_per_sec; - unsigned int i; - int ret; - - mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) + - load_per_type(core, VIDC_SESSION_TYPE_DEC); - - if (mbs_per_sec > core->res->max_load) - dev_warn(dev, "HW is overloaded, needed: %d max: %d\n", - mbs_per_sec, core->res->max_load); - - if (!mbs_per_sec && num_rows > 1) { - freq = table[num_rows - 1].freq; - goto set_freq; - } - - for (i = 0; i < num_rows; i++) { - if (mbs_per_sec > table[i].load) - break; - freq = table[i].freq; - } - -set_freq: - - ret = set_clk_freq(core, freq); - if (ret) { - dev_err(dev, "failed to set clock rate %lu (%d)\n", - freq, ret); - return ret; - } - - ret = load_scale_bw(core); - if (ret) { - dev_err(dev, "failed to set bandwidth (%d)\n", - ret); - return ret; - } - - return 0; -} - -static unsigned long calculate_inst_freq(struct venus_inst *inst, - unsigned long filled_len) -{ - unsigned long vpp_freq = 0, vsp_freq = 0; - u32 fps = (u32)inst->fps; - u32 mbs_per_sec; - - mbs_per_sec = load_per_instance(inst) / fps; - - vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; - /* 21 / 20 is overhead factor */ - vpp_freq += vpp_freq / 20; - vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq; - - /* 10 / 7 is overhead factor */ - if (inst->session_type == VIDC_SESSION_TYPE_ENC) - vsp_freq += (inst->controls.enc.bitrate * 10) / 7; - else - vsp_freq += ((fps * filled_len * 8) * 10) / 7; - - return max(vpp_freq, vsp_freq); -} - -static int scale_clocks_v4(struct venus_inst *inst) -{ - struct venus_core *core = inst->core; - const struct freq_tbl *table = core->res->freq_tbl; - unsigned int num_rows = core->res->freq_tbl_size; - struct device *dev = core->dev; - unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; - unsigned long filled_len = 0; - int i, ret; - - for (i = 0; i < inst->num_input_bufs; i++) - filled_len = max(filled_len, inst->payloads[i]); - - if (inst->session_type == VIDC_SESSION_TYPE_DEC && !filled_len) - return 0; - - freq = calculate_inst_freq(inst, filled_len); - inst->clk_data.freq = freq; - - mutex_lock(&core->lock); - list_for_each_entry(inst, &core->instances, list) { - if (inst->clk_data.core_id == VIDC_CORE_ID_1) { - freq_core1 += inst->clk_data.freq; - } else if (inst->clk_data.core_id == VIDC_CORE_ID_2) { - freq_core2 += inst->clk_data.freq; - } else if (inst->clk_data.core_id == VIDC_CORE_ID_3) { - freq_core1 += inst->clk_data.freq; - freq_core2 += inst->clk_data.freq; - } - } - mutex_unlock(&core->lock); - - freq = max(freq_core1, freq_core2); - - if (freq >= table[0].freq) { - freq = table[0].freq; - dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n", - freq, table[0].freq); - goto set_freq; - } - - for (i = num_rows - 1 ; i >= 0; i--) { - if (freq <= table[i].freq) { - freq = table[i].freq; - break; - } - } - -set_freq: - - ret = set_clk_freq(core, freq); - if (ret) { - dev_err(dev, "failed to set clock rate %lu (%d)\n", - freq, ret); - return ret; - } - - ret = load_scale_bw(core); - if (ret) { - dev_err(dev, "failed to set bandwidth (%d)\n", - ret); - return ret; - } - - return 0; -} - -int venus_helper_load_scale_clocks(struct venus_inst *inst) -{ - if (IS_V4(inst->core)) - return scale_clocks_v4(inst); - - return scale_clocks(inst); -} -EXPORT_SYMBOL_GPL(venus_helper_load_scale_clocks); - static void fill_buffer_desc(const struct venus_buffer *buf, struct hfi_buffer_desc *bd, bool response) { @@ -718,7 +459,7 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) if (inst->session_type == VIDC_SESSION_TYPE_DEC) put_ts_metadata(inst, vbuf); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (inst->session_type == VIDC_SESSION_TYPE_ENC) fdata.buffer_type = HFI_BUFFER_OUTPUT; @@ -1360,7 +1101,7 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) venus_helper_free_dpb_bufs(inst); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); INIT_LIST_HEAD(&inst->registeredbufs); } @@ -1423,7 +1164,7 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst) if (ret) goto err_bufs_free; - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); ret = hfi_session_load_res(inst); if (ret) @@ -1548,52 +1289,3 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, return -EINVAL; } EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts); - -int venus_helper_power_enable(struct venus_core *core, u32 session_type, - bool enable) -{ - void __iomem *ctrl, *stat; - u32 val; - int ret; - - if (!IS_V3(core) && !IS_V4(core)) - return 0; - - if (IS_V3(core)) { - if (session_type == VIDC_SESSION_TYPE_DEC) - ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL; - else - ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL; - if (enable) - writel(0, ctrl); - else - writel(1, ctrl); - - return 0; - } - - if (session_type == VIDC_SESSION_TYPE_DEC) { - ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; - stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; - } else { - ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL; - stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS; - } - - if (enable) { - writel(0, ctrl); - - ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100); - if (ret) - return ret; - } else { - writel(1, ctrl); - - ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(venus_helper_power_enable); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 34dcd0c13f06..44b50914cd12 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -53,14 +53,11 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 fmt, u32 *out_fmt, u32 *out2_fmt, bool ubwc); int venus_helper_alloc_dpb_bufs(struct venus_inst *inst); int venus_helper_free_dpb_bufs(struct venus_inst *inst); -int venus_helper_power_enable(struct venus_core *core, u32 session_type, - bool enable); int venus_helper_intbufs_alloc(struct venus_inst *inst); int venus_helper_intbufs_free(struct venus_inst *inst); int venus_helper_intbufs_realloc(struct venus_inst *inst); int venus_helper_queue_dpb_bufs(struct venus_inst *inst); int venus_helper_unregister_bufs(struct venus_inst *inst); -int venus_helper_load_scale_clocks(struct venus_inst *inst); int venus_helper_process_initial_cap_bufs(struct venus_inst *inst); int venus_helper_process_initial_out_bufs(struct venus_inst *inst); void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us, diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c new file mode 100644 index 000000000000..455797e16ffa --- /dev/null +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -0,0 +1,800 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Ltd. + * + * Author: Stanimir Varbanov + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "hfi_venus_io.h" +#include "pm_helpers.h" + +static bool legacy_binding; + +static int core_clks_get(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *dev = core->dev; + unsigned int i; + + for (i = 0; i < res->clks_num; i++) { + core->clks[i] = devm_clk_get(dev, res->clks[i]); + if (IS_ERR(core->clks[i])) + return PTR_ERR(core->clks[i]); + } + + return 0; +} + +static int core_clks_enable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + for (i = 0; i < res->clks_num; i++) { + ret = clk_prepare_enable(core->clks[i]); + if (ret) + goto err; + } + + return 0; +err: + while (i--) + clk_disable_unprepare(core->clks[i]); + + return ret; +} + +static void core_clks_disable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + unsigned int i = res->clks_num; + + while (i--) + clk_disable_unprepare(core->clks[i]); +} + +static int core_clks_set_rate(struct venus_core *core, unsigned long freq) +{ + struct clk *clk = core->clks[0]; + int ret; + + ret = clk_set_rate(clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(core->vcodec0_clks[0], freq); + if (ret) + return ret; + + ret = clk_set_rate(core->vcodec1_clks[0], freq); + if (ret) + return ret; + + return 0; +} + +static int vcodec_clks_get(struct venus_core *core, struct device *dev, + struct clk **clks, const char * const *id) +{ + const struct venus_resources *res = core->res; + unsigned int i; + + for (i = 0; i < res->vcodec_clks_num; i++) { + if (!id[i]) + continue; + clks[i] = devm_clk_get(dev, id[i]); + if (IS_ERR(clks[i])) + return PTR_ERR(clks[i]); + } + + return 0; +} + +static int vcodec_clks_enable(struct venus_core *core, struct clk **clks) +{ + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + for (i = 0; i < res->vcodec_clks_num; i++) { + ret = clk_prepare_enable(clks[i]); + if (ret) + goto err; + } + + return 0; +err: + while (i--) + clk_disable_unprepare(clks[i]); + + return ret; +} + +static void vcodec_clks_disable(struct venus_core *core, struct clk **clks) +{ + const struct venus_resources *res = core->res; + unsigned int i = res->vcodec_clks_num; + + while (i--) + clk_disable_unprepare(clks[i]); +} + +static u32 load_per_instance(struct venus_inst *inst) +{ + u32 mbs; + + if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP)) + return 0; + + mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16); + + return mbs * inst->fps; +} + +static u32 load_per_type(struct venus_core *core, u32 session_type) +{ + struct venus_inst *inst = NULL; + u32 mbs_per_sec = 0; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->session_type != session_type) + continue; + + mbs_per_sec += load_per_instance(inst); + } + mutex_unlock(&core->lock); + + return mbs_per_sec; +} + +static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak) +{ + const struct venus_resources *res = inst->core->res; + const struct bw_tbl *bw_tbl; + unsigned int num_rows, i; + + *avg = 0; + *peak = 0; + + if (mbs == 0) + return; + + if (inst->session_type == VIDC_SESSION_TYPE_ENC) { + num_rows = res->bw_tbl_enc_size; + bw_tbl = res->bw_tbl_enc; + } else if (inst->session_type == VIDC_SESSION_TYPE_DEC) { + num_rows = res->bw_tbl_dec_size; + bw_tbl = res->bw_tbl_dec; + } else { + return; + } + + if (!bw_tbl || num_rows == 0) + return; + + for (i = 0; i < num_rows; i++) { + if (mbs > bw_tbl[i].mbs_per_sec) + break; + + if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) { + *avg = bw_tbl[i].avg_10bit; + *peak = bw_tbl[i].peak_10bit; + } else { + *avg = bw_tbl[i].avg; + *peak = bw_tbl[i].peak; + } + } +} + +static int load_scale_bw(struct venus_core *core) +{ + struct venus_inst *inst = NULL; + u32 mbs_per_sec, avg, peak, total_avg = 0, total_peak = 0; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + mbs_per_sec = load_per_instance(inst); + mbs_to_bw(inst, mbs_per_sec, &avg, &peak); + total_avg += avg; + total_peak += peak; + } + mutex_unlock(&core->lock); + + dev_dbg(core->dev, "total: avg_bw: %u, peak_bw: %u\n", + total_avg, total_peak); + + return icc_set_bw(core->video_path, total_avg, total_peak); +} + +static int load_scale_v1(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct freq_tbl *table = core->res->freq_tbl; + unsigned int num_rows = core->res->freq_tbl_size; + unsigned long freq = table[0].freq; + struct device *dev = core->dev; + u32 mbs_per_sec; + unsigned int i; + int ret; + + mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) + + load_per_type(core, VIDC_SESSION_TYPE_DEC); + + if (mbs_per_sec > core->res->max_load) + dev_warn(dev, "HW is overloaded, needed: %d max: %d\n", + mbs_per_sec, core->res->max_load); + + if (!mbs_per_sec && num_rows > 1) { + freq = table[num_rows - 1].freq; + goto set_freq; + } + + for (i = 0; i < num_rows; i++) { + if (mbs_per_sec > table[i].load) + break; + freq = table[i].freq; + } + +set_freq: + + ret = core_clks_set_rate(core, freq); + if (ret) { + dev_err(dev, "failed to set clock rate %lu (%d)\n", + freq, ret); + return ret; + } + + ret = load_scale_bw(core); + if (ret) { + dev_err(dev, "failed to set bandwidth (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int core_get_v1(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return core_clks_get(core); +} + +static int core_power_v1(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + if (on == POWER_ON) + ret = core_clks_enable(core); + else + core_clks_disable(core); + + return ret; +} + +static const struct venus_pm_ops pm_ops_v1 = { + .core_get = core_get_v1, + .core_power = core_power_v1, + .load_scale = load_scale_v1, +}; + +static void +vcodec_control_v3(struct venus_core *core, u32 session_type, bool enable) +{ + void __iomem *ctrl; + + if (session_type == VIDC_SESSION_TYPE_DEC) + ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL; + else + ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL; + + if (enable) + writel(0, ctrl); + else + writel(1, ctrl); +} + +static int vdec_get_v3(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return vcodec_clks_get(core, dev, core->vcodec0_clks, + core->res->vcodec0_clks); +} + +static int vdec_power_v3(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + vcodec_control_v3(core, VIDC_SESSION_TYPE_DEC, true); + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec0_clks); + else + vcodec_clks_disable(core, core->vcodec0_clks); + + vcodec_control_v3(core, VIDC_SESSION_TYPE_DEC, false); + + return ret; +} + +static int venc_get_v3(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return vcodec_clks_get(core, dev, core->vcodec1_clks, + core->res->vcodec1_clks); +} + +static int venc_power_v3(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + vcodec_control_v3(core, VIDC_SESSION_TYPE_ENC, true); + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec1_clks); + else + vcodec_clks_disable(core, core->vcodec1_clks); + + vcodec_control_v3(core, VIDC_SESSION_TYPE_ENC, false); + + return ret; +} + +static const struct venus_pm_ops pm_ops_v3 = { + .core_get = core_get_v1, + .core_power = core_power_v1, + .vdec_get = vdec_get_v3, + .vdec_power = vdec_power_v3, + .venc_get = venc_get_v3, + .venc_power = venc_power_v3, + .load_scale = load_scale_v1, +}; + +static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable) +{ + void __iomem *ctrl, *stat; + u32 val; + int ret; + + if (coreid == VIDC_CORE_ID_1) { + ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; + stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; + } else { + ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL; + stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS; + } + + if (enable) { + writel(0, ctrl); + + ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100); + if (ret) + return ret; + } else { + writel(1, ctrl); + + ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100); + if (ret) + return ret; + } + + return 0; +} + +static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) +{ + int ret; + + if (coreid_mask & VIDC_CORE_ID_1) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + + vcodec_clks_disable(core, core->vcodec0_clks); + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); + if (ret) + return ret; + + ret = pm_runtime_put_sync(core->pmdomains[1]); + if (ret < 0) + return ret; + } + + if (coreid_mask & VIDC_CORE_ID_2) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + + vcodec_clks_disable(core, core->vcodec1_clks); + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); + if (ret) + return ret; + + ret = pm_runtime_put_sync(core->pmdomains[2]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) +{ + int ret; + + if (coreid_mask & VIDC_CORE_ID_1) { + ret = pm_runtime_get_sync(core->pmdomains[1]); + if (ret < 0) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + + ret = vcodec_clks_enable(core, core->vcodec0_clks); + if (ret) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); + if (ret < 0) + return ret; + } + + if (coreid_mask & VIDC_CORE_ID_2) { + ret = pm_runtime_get_sync(core->pmdomains[2]); + if (ret < 0) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + + ret = vcodec_clks_enable(core, core->vcodec1_clks); + if (ret) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); + if (ret < 0) + return ret; + } + + return 0; +} + +static int vdec_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (!legacy_binding) + return 0; + + return vcodec_clks_get(core, dev, core->vcodec0_clks, + core->res->vcodec0_clks); +} + +static void vdec_put_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + unsigned int i; + + if (!legacy_binding) + return; + + for (i = 0; i < core->res->vcodec_clks_num; i++) + core->vcodec0_clks[i] = NULL; +} + +static int vdec_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (!legacy_binding) + return 0; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec0_clks); + else + vcodec_clks_disable(core, core->vcodec0_clks); + + vcodec_control_v4(core, VIDC_CORE_ID_1, false); + + return ret; +} + +static int venc_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (!legacy_binding) + return 0; + + return vcodec_clks_get(core, dev, core->vcodec1_clks, + core->res->vcodec1_clks); +} + +static void venc_put_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + unsigned int i; + + if (!legacy_binding) + return; + + for (i = 0; i < core->res->vcodec_clks_num; i++) + core->vcodec1_clks[i] = NULL; +} + +static int venc_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (!legacy_binding) + return 0; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec1_clks); + else + vcodec_clks_disable(core, core->vcodec1_clks); + + vcodec_control_v4(core, VIDC_CORE_ID_2, false); + + return ret; +} + +static int vcodec_domains_get(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + struct device *pd; + unsigned int i; + + if (!res->vcodec_pmdomains_num) + return -ENODEV; + + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + pd = dev_pm_domain_attach_by_name(dev, + res->vcodec_pmdomains[i]); + if (IS_ERR(pd)) + return PTR_ERR(pd); + core->pmdomains[i] = pd; + } + + core->pd_dl_venus = device_link_add(dev, core->pmdomains[0], + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS | + DL_FLAG_RPM_ACTIVE); + if (!core->pd_dl_venus) + return -ENODEV; + + return 0; +} + +static void vcodec_domains_put(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + unsigned int i; + + if (!res->vcodec_pmdomains_num) + return; + + if (core->pd_dl_venus) + device_link_del(core->pd_dl_venus); + + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + if (IS_ERR_OR_NULL(core->pmdomains[i])) + continue; + dev_pm_domain_detach(core->pmdomains[i], true); + } +} + +static int core_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + int ret; + + ret = core_clks_get(core); + if (ret) + return ret; + + if (!res->vcodec_pmdomains_num) + legacy_binding = true; + + dev_info(dev, "%s legacy binding\n", legacy_binding ? "" : "non"); + + ret = vcodec_clks_get(core, dev, core->vcodec0_clks, res->vcodec0_clks); + if (ret) + return ret; + + ret = vcodec_clks_get(core, dev, core->vcodec1_clks, res->vcodec1_clks); + if (ret) + return ret; + + if (legacy_binding) + return 0; + + ret = vcodec_domains_get(dev); + if (ret) + return ret; + + return 0; +} + +static void core_put_v4(struct device *dev) +{ + if (legacy_binding) + return; + + vcodec_domains_put(dev); +} + +static int core_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + const unsigned int coreid_mask = VIDC_CORE_ID_1 | VIDC_CORE_ID_2; + int ret = 0; + + if (on == POWER_ON) { + ret = core_clks_enable(core); + if (ret) + return ret; + + if (!legacy_binding) + ret = poweron_coreid(core, coreid_mask); + } else { + if (!legacy_binding) + ret = poweroff_coreid(core, coreid_mask); + + core_clks_disable(core); + } + + return ret; +} + +static unsigned long calculate_inst_freq(struct venus_inst *inst, + unsigned long filled_len) +{ + unsigned long vpp_freq = 0, vsp_freq = 0; + u32 fps = (u32)inst->fps; + u32 mbs_per_sec; + + mbs_per_sec = load_per_instance(inst) / fps; + + vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; + /* 21 / 20 is overhead factor */ + vpp_freq += vpp_freq / 20; + vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq; + + /* 10 / 7 is overhead factor */ + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + vsp_freq += (inst->controls.enc.bitrate * 10) / 7; + else + vsp_freq += ((fps * filled_len * 8) * 10) / 7; + + return max(vpp_freq, vsp_freq); +} + +static int load_scale_v4(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct freq_tbl *table = core->res->freq_tbl; + unsigned int num_rows = core->res->freq_tbl_size; + struct device *dev = core->dev; + unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; + unsigned long filled_len = 0; + int i, ret; + + for (i = 0; i < inst->num_input_bufs; i++) + filled_len = max(filled_len, inst->payloads[i]); + + if (inst->session_type == VIDC_SESSION_TYPE_DEC && !filled_len) + return 0; + + freq = calculate_inst_freq(inst, filled_len); + inst->clk_data.freq = freq; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->clk_data.core_id == VIDC_CORE_ID_1) { + freq_core1 += inst->clk_data.freq; + } else if (inst->clk_data.core_id == VIDC_CORE_ID_2) { + freq_core2 += inst->clk_data.freq; + } else if (inst->clk_data.core_id == VIDC_CORE_ID_3) { + freq_core1 += inst->clk_data.freq; + freq_core2 += inst->clk_data.freq; + } + } + mutex_unlock(&core->lock); + + freq = max(freq_core1, freq_core2); + + if (freq >= table[0].freq) { + freq = table[0].freq; + dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n", + freq, table[0].freq); + goto set_freq; + } + + for (i = num_rows - 1 ; i >= 0; i--) { + if (freq <= table[i].freq) { + freq = table[i].freq; + break; + } + } + +set_freq: + + ret = core_clks_set_rate(core, freq); + if (ret) { + dev_err(dev, "failed to set clock rate %lu (%d)\n", + freq, ret); + return ret; + } + + ret = load_scale_bw(core); + if (ret) { + dev_err(dev, "failed to set bandwidth (%d)\n", + ret); + return ret; + } + + return 0; +} + +static const struct venus_pm_ops pm_ops_v4 = { + .core_get = core_get_v4, + .core_put = core_put_v4, + .core_power = core_power_v4, + .vdec_get = vdec_get_v4, + .vdec_put = vdec_put_v4, + .vdec_power = vdec_power_v4, + .venc_get = venc_get_v4, + .venc_put = venc_put_v4, + .venc_power = venc_power_v4, + .load_scale = load_scale_v4, +}; + +const struct venus_pm_ops *venus_pm_get(enum hfi_version version) +{ + switch (version) { + case HFI_VERSION_1XX: + default: + return &pm_ops_v1; + case HFI_VERSION_3XX: + return &pm_ops_v3; + case HFI_VERSION_4XX: + return &pm_ops_v4; + } + + return NULL; +} diff --git a/drivers/media/platform/qcom/venus/pm_helpers.h b/drivers/media/platform/qcom/venus/pm_helpers.h new file mode 100644 index 000000000000..d98b6dc20740 --- /dev/null +++ b/drivers/media/platform/qcom/venus/pm_helpers.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2019 Linaro Ltd. */ +#ifndef __VENUS_PM_HELPERS_H__ +#define __VENUS_PM_HELPERS_H__ + +struct device; + +#define POWER_ON 1 +#define POWER_OFF 0 + +struct venus_pm_ops { + int (*core_get)(struct device *dev); + void (*core_put)(struct device *dev); + int (*core_power)(struct device *dev, int on); + + int (*vdec_get)(struct device *dev); + void (*vdec_put)(struct device *dev); + int (*vdec_power)(struct device *dev, int on); + + int (*venc_get)(struct device *dev); + void (*venc_put)(struct device *dev); + int (*venc_power)(struct device *dev, int on); + + int (*load_scale)(struct venus_inst *inst); +}; + +const struct venus_pm_ops *venus_pm_get(enum hfi_version version); + +static inline int venus_pm_load_scale(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + + if (!core->pm_ops || !core->pm_ops->load_scale) + return 0; + + return core->pm_ops->load_scale(inst); +} + +#endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 32f0abd4f2fe..5433675c14af 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -20,6 +20,7 @@ #include "core.h" #include "helpers.h" #include "vdec.h" +#include "pm_helpers.h" /* * Three resons to keep MPLANE formats (despite that the number of planes @@ -868,7 +869,7 @@ reconfigure: if (ret) goto free_dpb_bufs; - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); ret = hfi_session_continue(inst); if (ret) @@ -1076,7 +1077,7 @@ static void vdec_session_release(struct venus_inst *inst) hfi_session_abort(inst); venus_helper_free_dpb_bufs(inst); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); INIT_LIST_HEAD(&inst->registeredbufs); mutex_unlock(&inst->lock); @@ -1432,20 +1433,14 @@ static int vdec_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (IS_V3(core) || IS_V4(core)) { - core->core0_clk = devm_clk_get(dev, "core"); - if (IS_ERR(core->core0_clk)) - return PTR_ERR(core->core0_clk); - } - - if (IS_V4(core)) { - core->core0_bus_clk = devm_clk_get(dev, "bus"); - if (IS_ERR(core->core0_bus_clk)) - return PTR_ERR(core->core0_bus_clk); - } - platform_set_drvdata(pdev, core); + if (core->pm_ops->vdec_get) { + ret = core->pm_ops->vdec_get(dev); + if (ret) + return ret; + } + vdev = video_device_alloc(); if (!vdev) return -ENOMEM; @@ -1482,57 +1477,33 @@ static int vdec_remove(struct platform_device *pdev) video_unregister_device(core->vdev_dec); pm_runtime_disable(core->dev_dec); + if (core->pm_ops->vdec_put) + core->pm_ops->vdec_put(core->dev_dec); + return 0; } static __maybe_unused int vdec_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - if (IS_V1(core)) - return 0; + if (pm_ops->vdec_power) + ret = pm_ops->vdec_power(dev, POWER_OFF); - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); - if (ret) - return ret; - - if (IS_V4(core)) - clk_disable_unprepare(core->core0_bus_clk); - - clk_disable_unprepare(core->core0_clk); - - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + return ret; } static __maybe_unused int vdec_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - if (IS_V1(core)) - return 0; + if (pm_ops->vdec_power) + ret = pm_ops->vdec_power(dev, POWER_ON); - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); - if (ret) - return ret; - - ret = clk_prepare_enable(core->core0_clk); - if (ret) - goto err_power_disable; - - if (IS_V4(core)) - ret = clk_prepare_enable(core->core0_bus_clk); - - if (ret) - goto err_unprepare_core0; - - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); - -err_unprepare_core0: - clk_disable_unprepare(core->core0_clk); -err_power_disable: - venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); return ret; } diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index adaa2237fd8f..a74a6ef33bfa 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -20,6 +20,7 @@ #include "core.h" #include "helpers.h" #include "venc.h" +#include "pm_helpers.h" #define NUM_B_FRAMES_MAX 4 @@ -1255,20 +1256,14 @@ static int venc_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (IS_V3(core) || IS_V4(core)) { - core->core1_clk = devm_clk_get(dev, "core"); - if (IS_ERR(core->core1_clk)) - return PTR_ERR(core->core1_clk); - } - - if (IS_V4(core)) { - core->core1_bus_clk = devm_clk_get(dev, "bus"); - if (IS_ERR(core->core1_bus_clk)) - return PTR_ERR(core->core1_bus_clk); - } - platform_set_drvdata(pdev, core); + if (core->pm_ops->venc_get) { + ret = core->pm_ops->venc_get(dev); + if (ret) + return ret; + } + vdev = video_device_alloc(); if (!vdev) return -ENOMEM; @@ -1305,57 +1300,33 @@ static int venc_remove(struct platform_device *pdev) video_unregister_device(core->vdev_enc); pm_runtime_disable(core->dev_enc); + if (core->pm_ops->venc_put) + core->pm_ops->venc_put(core->dev_enc); + return 0; } static __maybe_unused int venc_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - if (IS_V1(core)) - return 0; + if (pm_ops->venc_power) + ret = pm_ops->venc_power(dev, POWER_OFF); - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); - if (ret) - return ret; - - if (IS_V4(core)) - clk_disable_unprepare(core->core1_bus_clk); - - clk_disable_unprepare(core->core1_clk); - - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); + return ret; } static __maybe_unused int venc_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - if (IS_V1(core)) - return 0; + if (pm_ops->venc_power) + ret = pm_ops->venc_power(dev, POWER_ON); - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); - if (ret) - return ret; - - ret = clk_prepare_enable(core->core1_clk); - if (ret) - goto err_power_disable; - - if (IS_V4(core)) - ret = clk_prepare_enable(core->core1_bus_clk); - - if (ret) - goto err_unprepare_core1; - - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); - -err_unprepare_core1: - clk_disable_unprepare(core->core1_clk); -err_power_disable: - venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); return ret; } From 4ebf969375bca2cef03573d23416423cec1af814 Mon Sep 17 00:00:00 2001 From: Aniket Masule Date: Mon, 2 Dec 2019 17:44:41 +0100 Subject: [PATCH 0678/2231] media: venus: introduce core selection Presently the core (vcodec pipelines) assignment is static. Here we introduce dynamic load balancing across the cores depending on the current session load. The load on earch core is calculated and core with minimum load is assigned to given instance. This will be applicable on Venus v4 with more than one vcodec cores. Signed-off-by: Aniket Masule Co-developed-by: Stanimir Varbanov Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 3 + drivers/media/platform/qcom/venus/helpers.c | 17 +- drivers/media/platform/qcom/venus/helpers.h | 1 - .../media/platform/qcom/venus/hfi_helper.h | 1 + .../media/platform/qcom/venus/hfi_parser.h | 5 + .../media/platform/qcom/venus/pm_helpers.c | 183 ++++++++++++++++-- .../media/platform/qcom/venus/pm_helpers.h | 26 +++ drivers/media/platform/qcom/venus/vdec.c | 11 +- drivers/media/platform/qcom/venus/venc.c | 10 +- 9 files changed, 221 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index e587fd7e13c3..bba29e4680f9 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -181,6 +181,8 @@ struct venus_core { struct delayed_work work; struct venus_caps caps[MAX_CODEC_NUM]; unsigned int codecs_count; + unsigned int core0_usage_count; + unsigned int core1_usage_count; }; struct vdec_controls { @@ -380,6 +382,7 @@ struct venus_inst { const struct hfi_inst_ops *ops; u32 session_type; union hfi_get_property hprop; + unsigned int core_acquired: 1; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index aaf88b184efd..2b8c585432be 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -723,21 +723,6 @@ int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode) } EXPORT_SYMBOL_GPL(venus_helper_set_work_mode); -int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage) -{ - const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; - struct hfi_videocores_usage_type cu; - - inst->clk_data.core_id = usage; - if (!IS_V4(inst->core)) - return 0; - - cu.video_core_enable_mask = usage; - - return hfi_session_set_property(inst, ptype, &cu); -} -EXPORT_SYMBOL_GPL(venus_helper_set_core_usage); - int venus_helper_init_codec_freq_data(struct venus_inst *inst) { const struct codec_freq_data *data; @@ -1112,6 +1097,8 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) else inst->streamon_cap = 0; + venus_pm_release_core(inst); + mutex_unlock(&inst->lock); } EXPORT_SYMBOL_GPL(venus_helper_vb2_stop_streaming); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 44b50914cd12..b64875564064 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -34,7 +34,6 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, u32 buftype); int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); int venus_helper_init_codec_freq_data(struct venus_inst *inst); -int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs); diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index b70551e296b7..2a47f6c0a07a 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -550,6 +550,7 @@ struct hfi_bitrate { #define HFI_CAPABILITY_LCU_SIZE 0x14 #define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15 #define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16 +#define HFI_CAPABILITY_MAX_VIDEOCORES 0x2b struct hfi_capability { u32 capability_type; diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h index 3e931c747e19..264e6dd2415f 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.h +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -107,4 +107,9 @@ static inline u32 frate_step(struct venus_inst *inst) return cap_step(inst, HFI_CAPABILITY_FRAMERATE); } +static inline u32 core_num_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_MAX_VIDEOCORES); +} + #endif diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 455797e16ffa..abf93158857b 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -14,6 +14,7 @@ #include #include "core.h" +#include "hfi_parser.h" #include "hfi_venus_io.h" #include "pm_helpers.h" @@ -480,6 +481,173 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) return 0; } +static void +min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) +{ + u32 mbs_per_sec, load, core1_load = 0, core2_load = 0; + u32 cores_max = core_num_max(inst); + struct venus_core *core = inst->core; + struct venus_inst *inst_pos; + unsigned long vpp_freq; + u32 coreid; + + mutex_lock(&core->lock); + + list_for_each_entry(inst_pos, &core->instances, list) { + if (inst_pos == inst) + continue; + vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; + coreid = inst_pos->clk_data.core_id; + + mbs_per_sec = load_per_instance(inst_pos); + load = mbs_per_sec * vpp_freq; + + if ((coreid & VIDC_CORE_ID_3) == VIDC_CORE_ID_3) { + core1_load += load / 2; + core2_load += load / 2; + } else if (coreid & VIDC_CORE_ID_1) { + core1_load += load; + } else if (coreid & VIDC_CORE_ID_2) { + core2_load += load; + } + } + + *min_coreid = core1_load <= core2_load ? + VIDC_CORE_ID_1 : VIDC_CORE_ID_2; + *min_load = min(core1_load, core2_load); + + if (cores_max < VIDC_CORE_ID_2 || core->res->vcodec_num < 2) { + *min_coreid = VIDC_CORE_ID_1; + *min_load = core1_load; + } + + mutex_unlock(&core->lock); +} + +static int decide_core(struct venus_inst *inst) +{ + const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; + struct venus_core *core = inst->core; + u32 min_coreid, min_load, inst_load; + struct hfi_videocores_usage_type cu; + unsigned long max_freq; + + if (legacy_binding) { + if (inst->session_type == VIDC_SESSION_TYPE_DEC) + cu.video_core_enable_mask = VIDC_CORE_ID_1; + else + cu.video_core_enable_mask = VIDC_CORE_ID_2; + + goto done; + } + + if (inst->clk_data.core_id != VIDC_CORE_ID_DEFAULT) + return 0; + + inst_load = load_per_instance(inst); + inst_load *= inst->clk_data.codec_freq_data->vpp_freq; + max_freq = core->res->freq_tbl[0].freq; + + min_loaded_core(inst, &min_coreid, &min_load); + + if ((inst_load + min_load) > max_freq) { + dev_warn(core->dev, "HW is overloaded, needed: %u max: %lu\n", + inst_load, max_freq); + return -EINVAL; + } + + inst->clk_data.core_id = min_coreid; + cu.video_core_enable_mask = min_coreid; + +done: + return hfi_session_set_property(inst, ptype, &cu); +} + +static int acquire_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + unsigned int coreid_mask = 0; + + if (inst->core_acquired) + return 0; + + inst->core_acquired = true; + + if (inst->clk_data.core_id & VIDC_CORE_ID_1) { + if (core->core0_usage_count++) + return 0; + + coreid_mask = VIDC_CORE_ID_1; + } + + if (inst->clk_data.core_id & VIDC_CORE_ID_2) { + if (core->core1_usage_count++) + return 0; + + coreid_mask |= VIDC_CORE_ID_2; + } + + return poweron_coreid(core, coreid_mask); +} + +static int release_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + unsigned int coreid_mask = 0; + int ret; + + if (!inst->core_acquired) + return 0; + + if (inst->clk_data.core_id & VIDC_CORE_ID_1) { + if (--core->core0_usage_count) + goto done; + + coreid_mask = VIDC_CORE_ID_1; + } + + if (inst->clk_data.core_id & VIDC_CORE_ID_2) { + if (--core->core1_usage_count) + goto done; + + coreid_mask |= VIDC_CORE_ID_2; + } + + ret = poweroff_coreid(core, coreid_mask); + if (ret) + return ret; + +done: + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; + return 0; +} + +static int coreid_power_v4(struct venus_inst *inst, int on) +{ + struct venus_core *core = inst->core; + int ret; + + if (legacy_binding) + return 0; + + if (on == POWER_ON) { + ret = decide_core(inst); + if (ret) + return ret; + + mutex_lock(&core->lock); + ret = acquire_core(inst); + mutex_unlock(&core->lock); + } else { + mutex_lock(&core->lock); + ret = release_core(inst); + mutex_unlock(&core->lock); + } + + return ret; +} + static int vdec_get_v4(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); @@ -661,22 +829,12 @@ static void core_put_v4(struct device *dev) static int core_power_v4(struct device *dev, int on) { struct venus_core *core = dev_get_drvdata(dev); - const unsigned int coreid_mask = VIDC_CORE_ID_1 | VIDC_CORE_ID_2; int ret = 0; - if (on == POWER_ON) { + if (on == POWER_ON) ret = core_clks_enable(core); - if (ret) - return ret; - - if (!legacy_binding) - ret = poweron_coreid(core, coreid_mask); - } else { - if (!legacy_binding) - ret = poweroff_coreid(core, coreid_mask); - + else core_clks_disable(core); - } return ret; } @@ -781,6 +939,7 @@ static const struct venus_pm_ops pm_ops_v4 = { .venc_get = venc_get_v4, .venc_put = venc_put_v4, .venc_power = venc_power_v4, + .coreid_power = coreid_power_v4, .load_scale = load_scale_v4, }; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.h b/drivers/media/platform/qcom/venus/pm_helpers.h index d98b6dc20740..aa2f6afa2354 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.h +++ b/drivers/media/platform/qcom/venus/pm_helpers.h @@ -21,6 +21,8 @@ struct venus_pm_ops { void (*venc_put)(struct device *dev); int (*venc_power)(struct device *dev, int on); + int (*coreid_power)(struct venus_inst *inst, int on); + int (*load_scale)(struct venus_inst *inst); }; @@ -36,4 +38,28 @@ static inline int venus_pm_load_scale(struct venus_inst *inst) return core->pm_ops->load_scale(inst); } +static inline int venus_pm_acquire_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; + + if (pm_ops && pm_ops->coreid_power) + ret = pm_ops->coreid_power(inst, POWER_ON); + + return ret; +} + +static inline int venus_pm_release_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; + + if (pm_ops && pm_ops->coreid_power) + ret = pm_ops->coreid_power(inst, POWER_OFF); + + return ret; +} + #endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 5433675c14af..5e0a912d387e 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -579,10 +579,6 @@ static int vdec_output_conf(struct venus_inst *inst) if (ret) return ret; - ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_1); - if (ret) - return ret; - if (core->res->hfi_version == HFI_VERSION_1XX) { ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER; ret = hfi_session_set_property(inst, ptype, &en); @@ -951,6 +947,10 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&inst->lock); + ret = venus_pm_acquire_core(inst); + if (ret) + return ret; + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ret = vdec_start_capture(inst); else @@ -1078,6 +1078,7 @@ static void vdec_session_release(struct venus_inst *inst) venus_helper_free_dpb_bufs(inst); venus_pm_load_scale(inst); + venus_pm_release_core(inst); INIT_LIST_HEAD(&inst->registeredbufs); mutex_unlock(&inst->lock); @@ -1337,6 +1338,8 @@ static int vdec_open(struct file *file) inst->num_output_bufs = 1; inst->codec_state = VENUS_DEC_STATE_DEINIT; inst->buf_count = 0; + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index a74a6ef33bfa..b6f242c4cb2c 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -656,10 +656,6 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; - ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_2); - if (ret) - return ret; - ptype = HFI_PROPERTY_CONFIG_FRAME_RATE; frate.buffer_type = HFI_BUFFER_OUTPUT; frate.framerate = inst->fps * (1 << 16); @@ -992,6 +988,10 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) if (ret) goto bufs_done; + ret = venus_pm_acquire_core(inst); + if (ret) + goto deinit_sess; + ret = venc_set_properties(inst); if (ret) goto deinit_sess; @@ -1160,6 +1160,8 @@ static int venc_open(struct file *file) inst->core = core; inst->session_type = VIDC_SESSION_TYPE_ENC; + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; venus_helper_init_instance(inst); From ab1eda449c6eef5067538b034c7561eb9e053362 Mon Sep 17 00:00:00 2001 From: Aniket Masule Date: Wed, 4 Dec 2019 11:18:40 +0100 Subject: [PATCH 0679/2231] media: venus: vdec: handle 10bit bitstreams Handle 10bit video streams in the decoder by using dithering, i.e the decoder output buffers will be in 8bit format. The runtime handling is implemented by sending v4l2 event to userspace application, then the application should stop the streaming on capture queue and initiate format negotiation, and start streaming again. Signed-off-by: Aniket Masule Co-developed-by: Stanimir Varbanov Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.h | 1 + drivers/media/platform/qcom/venus/helpers.c | 99 +++++++++++++++++++ .../media/platform/qcom/venus/hfi_helper.h | 5 + drivers/media/platform/qcom/venus/vdec.c | 4 + 4 files changed, 109 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index bba29e4680f9..ab7c360fa484 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -383,6 +383,7 @@ struct venus_inst { u32 session_type; union hfi_get_property hprop; unsigned int core_acquired: 1; + unsigned int bit_depth; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 2b8c585432be..bcc603804041 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -626,6 +626,78 @@ static u32 get_framesize_raw_nv12_ubwc(u32 width, u32 height) max(extradata, y_stride * 48), SZ_4K); } +static u32 get_framesize_raw_p010(u32 width, u32 height) +{ + u32 y_plane, uv_plane, y_stride, uv_stride, y_sclines, uv_sclines; + + y_stride = ALIGN(width * 2, 256); + uv_stride = ALIGN(width * 2, 256); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN((height + 1) >> 1, 16); + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines; + + return ALIGN((y_plane + uv_plane), SZ_4K); +} + +static u32 get_framesize_raw_p010_ubwc(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_sclines, uv_sclines; + u32 y_ubwc_plane, uv_ubwc_plane; + u32 y_meta_stride, y_meta_scanlines; + u32 uv_meta_stride, uv_meta_scanlines; + u32 y_meta_plane, uv_meta_plane; + u32 size; + + y_stride = ALIGN(width * 2, 256); + uv_stride = ALIGN(width * 2, 256); + y_sclines = ALIGN(height, 16); + uv_sclines = ALIGN((height + 1) >> 1, 16); + + y_ubwc_plane = ALIGN(y_stride * y_sclines, SZ_4K); + uv_ubwc_plane = ALIGN(uv_stride * uv_sclines, SZ_4K); + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 32), 64); + y_meta_scanlines = ALIGN(DIV_ROUND_UP(height, 4), 16); + y_meta_plane = ALIGN(y_meta_stride * y_meta_scanlines, SZ_4K); + uv_meta_stride = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 16), 64); + uv_meta_scanlines = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + uv_meta_plane = ALIGN(uv_meta_stride * uv_meta_scanlines, SZ_4K); + + size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane; + + return ALIGN(size, SZ_4K); +} + +static u32 get_framesize_raw_yuv420_tp10_ubwc(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_sclines, uv_sclines; + u32 y_ubwc_plane, uv_ubwc_plane; + u32 y_meta_stride, y_meta_scanlines; + u32 uv_meta_stride, uv_meta_scanlines; + u32 y_meta_plane, uv_meta_plane; + u32 extradata = SZ_16K; + u32 size; + + y_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256); + uv_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256); + y_sclines = ALIGN(height, 16); + uv_sclines = ALIGN((height + 1) >> 1, 16); + + y_ubwc_plane = ALIGN(y_stride * y_sclines, SZ_4K); + uv_ubwc_plane = ALIGN(uv_stride * uv_sclines, SZ_4K); + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 48), 64); + y_meta_scanlines = ALIGN(DIV_ROUND_UP(height, 4), 16); + y_meta_plane = ALIGN(y_meta_stride * y_meta_scanlines, SZ_4K); + uv_meta_stride = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 24), 64); + uv_meta_scanlines = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + uv_meta_plane = ALIGN(uv_meta_stride * uv_meta_scanlines, SZ_4K); + + size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane; + size += max(extradata + SZ_8K, y_stride * 48); + + return ALIGN(size, SZ_4K); +} + u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) { switch (hfi_fmt) { @@ -634,6 +706,12 @@ u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) return get_framesize_raw_nv12(width, height); case HFI_COLOR_FORMAT_NV12_UBWC: return get_framesize_raw_nv12_ubwc(width, height); + case HFI_COLOR_FORMAT_P010: + return get_framesize_raw_p010(width, height); + case HFI_COLOR_FORMAT_P010_UBWC: + return get_framesize_raw_p010_ubwc(width, height); + case HFI_COLOR_FORMAT_YUV420_TP10_UBWC: + return get_framesize_raw_yuv420_tp10_ubwc(width, height); default: return 0; } @@ -1246,6 +1324,27 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, if (!caps) return -EINVAL; + if (inst->bit_depth == VIDC_BITDEPTH_10 && + inst->session_type == VIDC_SESSION_TYPE_DEC) { + found_ubwc = + find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, + HFI_COLOR_FORMAT_YUV420_TP10_UBWC); + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, + HFI_COLOR_FORMAT_NV12); + if (found_ubwc && found) { + /* + * Hard-code DPB buffers to be 10bit UBWC and decoder + * output buffers in 8bit NV12 until V4L2 is able to + * expose compressed/tiled formats to applications. + */ + *out_fmt = HFI_COLOR_FORMAT_YUV420_TP10_UBWC; + *out2_fmt = HFI_COLOR_FORMAT_NV12; + return 0; + } + + return -EINVAL; + } + if (ubwc) { ubwc_fmt = fmt | HFI_COLOR_FORMAT_UBWC_BASE; found_ubwc = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 2a47f6c0a07a..f6613df1d16b 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -793,6 +793,9 @@ struct hfi_h264_vui_timing_info { u32 time_scale; }; +#define VIDC_BITDEPTH_8 0x00000 +#define VIDC_BITDEPTH_10 0x20002 + struct hfi_bit_depth { u32 buffer_type; u32 bit_depth; @@ -841,8 +844,10 @@ struct hfi_extradata_input_crop { #define HFI_COLOR_FORMAT_10_BIT_BASE 0x4000 #define HFI_COLOR_FORMAT_YUV420_TP10 0x4002 +#define HFI_COLOR_FORMAT_P010 0x4003 #define HFI_COLOR_FORMAT_NV12_UBWC 0x8002 #define HFI_COLOR_FORMAT_YUV420_TP10_UBWC 0xc002 +#define HFI_COLOR_FORMAT_P010_UBWC 0xc003 #define HFI_COLOR_FORMAT_RGBA8888_UBWC 0x8010 struct hfi_uncompressed_format_select { diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 5e0a912d387e..f349201c0f0d 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1193,6 +1193,9 @@ static void vdec_event_change(struct venus_inst *inst, inst->out_width = ev_data->width; inst->out_height = ev_data->height; + if (inst->bit_depth != ev_data->bit_depth) + inst->bit_depth = ev_data->bit_depth; + dev_dbg(dev, "event %s sufficient resources (%ux%u)\n", sufficient ? "" : "not", ev_data->width, ev_data->height); @@ -1340,6 +1343,7 @@ static int vdec_open(struct file *file) inst->buf_count = 0; inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; inst->core_acquired = false; + inst->bit_depth = VIDC_BITDEPTH_8; init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); From 146b5bbf01bee29eefca152c1638c2d1cce90a28 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 17 Dec 2019 14:20:46 +0100 Subject: [PATCH 0680/2231] media: dt-bindings: media: venus: Convert msm8916 to DT schema Convert qcom,msm8916-venus Venus binding to DT schema Reviewed-by: Rob Herring Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/qcom,msm8916-venus.yaml | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,msm8916-venus.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,msm8916-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8916-venus.yaml new file mode 100644 index 000000000000..f9606df02d70 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,msm8916-venus.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,msm8916-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,msm8916-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + clocks: + maxItems: 3 + + clock-names: + items: + - const: core + - const: iface + - const: bus + + iommus: + maxItems: 1 + + memory-region: + maxItems: 1 + + video-decoder: + type: object + + properties: + compatible: + const: "venus-decoder" + + required: + - compatible + + additionalProperties: false + + video-encoder: + type: object + + properties: + compatible: + const: "venus-encoder" + + required: + - compatible + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - clocks + - clock-names + - iommus + - memory-region + - video-decoder + - video-encoder + +examples: + - | + #include + #include + + video-codec@1d00000 { + compatible = "qcom,msm8916-venus"; + reg = <0x01d00000 0xff000>; + interrupts = ; + clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_AHB_CLK>, + <&gcc GCC_VENUS0_AXI_CLK>; + clock-names = "core", "iface", "bus"; + power-domains = <&gcc VENUS_GDSC>; + iommus = <&apps_iommu 5>; + memory-region = <&venus_mem>; + + video-decoder { + compatible = "venus-decoder"; + }; + + video-encoder { + compatible = "venus-encoder"; + }; + }; From 12e21a23ccf3bdd1e3207001422235e24d865ac4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 27 Feb 2020 12:31:06 +0100 Subject: [PATCH 0681/2231] i3c: Fix MODALIAS uevents file2alias uses %X formatters. Fix typos in the MODALIAS uevent to print the part and ext IDs in uppercase. Signed-off-by: Boris Brezillon Signed-off-by: Vitor Soares Link: https://lore.kernel.org/linux-i3c/9ac5f1f8413fbb0481de76b5e43f2f4e1b2dc49f.1582796652.git.vitor.soares@synopsys.com --- drivers/i3c/master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index b56207bbed2b..b6db82862a63 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -267,7 +267,7 @@ static int i3c_device_uevent(struct device *dev, struct kobj_uevent_env *env) devinfo.dcr, manuf); return add_uevent_var(env, - "MODALIAS=i3c:dcr%02Xmanuf%04Xpart%04xext%04x", + "MODALIAS=i3c:dcr%02Xmanuf%04Xpart%04Xext%04X", devinfo.dcr, manuf, part, ext); } From df7acc7e7ed440be1a7713baf46a15c71b5a6a77 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 17 Dec 2019 14:24:55 +0100 Subject: [PATCH 0682/2231] media: dt-bindings: media: venus: Convert msm8996 to DT schema Convert qcom,msm8996-venus Venus binding to DT schema. Reviewed-by: Rob Herring Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/qcom,msm8996-venus.yaml | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml new file mode 100644 index 000000000000..fa0dc6c47f1d --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml @@ -0,0 +1,172 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,msm8996-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,msm8996-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: mbus + + iommus: + maxItems: 20 + + memory-region: + maxItems: 1 + + video-decoder: + type: object + + properties: + compatible: + const: venus-decoder + + clocks: + maxItems: 1 + + clock-names: + items: + - const: core + + power-domains: + maxItems: 1 + + required: + - compatible + - clocks + - clock-names + - power-domains + + additionalProperties: false + + video-encoder: + type: object + + properties: + compatible: + const: venus-encoder + + clocks: + maxItems: 1 + + clock-names: + items: + - const: core + + power-domains: + maxItems: 1 + + required: + - compatible + - clocks + - clock-names + - power-domains + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - clocks + - clock-names + - iommus + - memory-region + - video-decoder + - video-encoder + +examples: + - | + #include + #include + + video-codec@c00000 { + compatible = "qcom,msm8996-venus"; + reg = <0x00c00000 0xff000>; + interrupts = ; + clocks = <&mmcc VIDEO_CORE_CLK>, + <&mmcc VIDEO_AHB_CLK>, + <&mmcc VIDEO_AXI_CLK>, + <&mmcc VIDEO_MAXI_CLK>; + clock-names = "core", "iface", "bus", "mbus"; + power-domains = <&mmcc VENUS_GDSC>; + iommus = <&venus_smmu 0x00>, + <&venus_smmu 0x01>, + <&venus_smmu 0x0a>, + <&venus_smmu 0x07>, + <&venus_smmu 0x0e>, + <&venus_smmu 0x0f>, + <&venus_smmu 0x08>, + <&venus_smmu 0x09>, + <&venus_smmu 0x0b>, + <&venus_smmu 0x0c>, + <&venus_smmu 0x0d>, + <&venus_smmu 0x10>, + <&venus_smmu 0x11>, + <&venus_smmu 0x21>, + <&venus_smmu 0x28>, + <&venus_smmu 0x29>, + <&venus_smmu 0x2b>, + <&venus_smmu 0x2c>, + <&venus_smmu 0x2d>, + <&venus_smmu 0x31>; + memory-region = <&venus_mem>; + + video-decoder { + compatible = "venus-decoder"; + clocks = <&mmcc VIDEO_SUBCORE0_CLK>; + clock-names = "core"; + power-domains = <&mmcc VENUS_CORE0_GDSC>; + }; + + video-encoder { + compatible = "venus-encoder"; + clocks = <&mmcc VIDEO_SUBCORE1_CLK>; + clock-names = "core"; + power-domains = <&mmcc VENUS_CORE1_GDSC>; + }; + }; From 49a3797aa51623f9891a832eff9dea89fb00c1c1 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 17 Dec 2019 14:25:34 +0100 Subject: [PATCH 0683/2231] media: dt-bindings: media: venus: Convert sdm845 to DT schema Convert qcom,sdm845-venus Venus binding to DT schema. Reviewed-by: Rob Herring Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/qcom,sdm845-venus.yaml | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,sdm845-venus.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,sdm845-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sdm845-venus.yaml new file mode 100644 index 000000000000..05cabe4e893a --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sdm845-venus.yaml @@ -0,0 +1,156 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,sdm845-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,sdm845-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + clocks: + maxItems: 3 + + clock-names: + items: + - const: core + - const: iface + - const: bus + + iommus: + maxItems: 2 + + memory-region: + maxItems: 1 + + video-core0: + type: object + + properties: + compatible: + const: venus-decoder + + clocks: + maxItems: 2 + + clock-names: + items: + - const: core + - const: bus + + power-domains: + maxItems: 1 + + required: + - compatible + - clocks + - clock-names + - power-domains + + additionalProperties: false + + video-core1: + type: object + + properties: + compatible: + const: venus-encoder + + clocks: + maxItems: 2 + + clock-names: + items: + - const: core + - const: bus + + power-domains: + maxItems: 1 + + required: + - compatible + - clocks + - clock-names + - power-domains + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - clocks + - clock-names + - iommus + - memory-region + - video-core0 + - video-core1 + +examples: + - | + #include + #include + + video-codec@aa00000 { + compatible = "qcom,sdm845-venus"; + reg = <0 0x0aa00000 0 0xff000>; + interrupts = ; + clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, + <&videocc VIDEO_CC_VENUS_AHB_CLK>, + <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>; + clock-names = "core", "iface", "bus"; + power-domains = <&videocc VENUS_GDSC>; + iommus = <&apps_smmu 0x10a0 0x8>, + <&apps_smmu 0x10b0 0x0>; + memory-region = <&venus_mem>; + + video-core0 { + compatible = "venus-decoder"; + clocks = <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC0_AXI_CLK>; + clock-names = "core", "bus"; + power-domains = <&videocc VCODEC0_GDSC>; + }; + + video-core1 { + compatible = "venus-encoder"; + clocks = <&videocc VIDEO_CC_VCODEC1_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC1_AXI_CLK>; + clock-names = "core", "bus"; + power-domains = <&videocc VCODEC1_GDSC>; + }; + }; From fde57f90a9838cf86e337cf3cd0e3a108ff222d5 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 17 Dec 2019 14:26:18 +0100 Subject: [PATCH 0684/2231] media: dt-bindings: media: venus: Add sdm845v2 DT schema Add new qcom,sdm845-venus-v2 DT binding schema. Reviewed-by: Rob Herring Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/qcom,sdm845-venus-v2.yaml | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,sdm845-venus-v2.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,sdm845-venus-v2.yaml b/Documentation/devicetree/bindings/media/qcom,sdm845-venus-v2.yaml new file mode 100644 index 000000000000..8552f4ab907e --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sdm845-venus-v2.yaml @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,sdm845-venus-v2.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,sdm845-venus-v2 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 3 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + - const: vcodec1 + + clocks: + maxItems: 7 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: vcodec0_core + - const: vcodec0_bus + - const: vcodec1_core + - const: vcodec1_bus + + iommus: + maxItems: 2 + + memory-region: + maxItems: 1 + + video-core0: + type: object + + properties: + compatible: + const: venus-decoder + + required: + - compatible + + additionalProperties: false + + video-core1: + type: object + + properties: + compatible: + const: venus-encoder + + required: + - compatible + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - power-domain-names + - clocks + - clock-names + - iommus + - memory-region + - video-core0 + - video-core1 + +examples: + - | + #include + #include + + video-codec@aa00000 { + compatible = "qcom,sdm845-venus-v2"; + reg = <0 0x0aa00000 0 0xff000>; + interrupts = ; + clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, + <&videocc VIDEO_CC_VENUS_AHB_CLK>, + <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, + <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC0_AXI_CLK>, + <&videocc VIDEO_CC_VCODEC1_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC1_AXI_CLK>; + clock-names = "core", "iface", "bus", + "vcodec0_core", "vcodec0_bus", + "vcodec1_core", "vcodec1_bus"; + power-domains = <&videocc VENUS_GDSC>, + <&videocc VCODEC0_GDSC>, + <&videocc VCODEC1_GDSC>; + power-domain-names = "venus", "vcodec0", "vcodec1"; + iommus = <&apps_smmu 0x10a0 0x8>, + <&apps_smmu 0x10b0 0x0>; + memory-region = <&venus_mem>; + + video-core0 { + compatible = "venus-decoder"; + }; + + video-core1 { + compatible = "venus-encoder"; + }; + }; From 799e4bc2914214b76ff97d3978b36353d1feaff7 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 18 Dec 2019 11:46:26 +0100 Subject: [PATCH 0685/2231] media: venus: core: add sdm845-v2 DT compatible and resource struct In order to use dynamic video codec assignment add a new sdm845-v2 DT compatible and new venus_resource structure to cover the binding where all pmdomains and clocks are under the venus_core control. Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index a20ab00a8068..c7525d951e92 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -495,10 +495,37 @@ static const struct venus_resources sdm845_res = { .fwname = "qcom/venus-5.2/venus.mdt", }; +static const struct venus_resources sdm845_res_v2 = { + .freq_tbl = sdm845_freq_table, + .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), + .bw_tbl_enc = sdm845_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), + .bw_tbl_dec = sdm845_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), + .codec_freq_data = sdm845_codec_freq_data, + .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), + .clks = {"core", "iface", "bus" }, + .clks_num = 3, + .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, + .vcodec1_clks = { "vcodec1_core", "vcodec1_bus" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = { "venus", "vcodec0", "vcodec1" }, + .vcodec_pmdomains_num = 3, + .vcodec_num = 2, + .max_load = 3110400, /* 4096x2160@90 */ + .hfi_version = HFI_VERSION_4XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/venus-5.2/venus.mdt", +}; + static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, + { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, { } }; MODULE_DEVICE_TABLE(of, venus_dt_match); From 0e1558deedad68cf3aae0e94ec50ece10c60ed9c Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 18 Dec 2019 14:04:54 +0100 Subject: [PATCH 0686/2231] media: dt-bindings: media: venus: delete old binding document After transitioning to YAML DT schema we don't need this old-style document. Acked-by: Rob Herring Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/qcom,venus.txt | 120 ------------------ 1 file changed, 120 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/qcom,venus.txt diff --git a/Documentation/devicetree/bindings/media/qcom,venus.txt b/Documentation/devicetree/bindings/media/qcom,venus.txt deleted file mode 100644 index b602c4c025e7..000000000000 --- a/Documentation/devicetree/bindings/media/qcom,venus.txt +++ /dev/null @@ -1,120 +0,0 @@ -* Qualcomm Venus video encoder/decoder accelerators - -- compatible: - Usage: required - Value type: - Definition: Value should contain one of: - - "qcom,msm8916-venus" - - "qcom,msm8996-venus" - - "qcom,sdm845-venus" -- reg: - Usage: required - Value type: - Definition: Register base address and length of the register map. -- interrupts: - Usage: required - Value type: - Definition: Should contain interrupt line number. -- clocks: - Usage: required - Value type: - Definition: A List of phandle and clock specifier pairs as listed - in clock-names property. -- clock-names: - Usage: required for msm8916 - Value type: - Definition: Should contain the following entries: - - "core" Core video accelerator clock - - "iface" Video accelerator AHB clock - - "bus" Video accelerator AXI clock -- clock-names: - Usage: required for msm8996 - Value type: - Definition: Should contain the following entries: - - "core" Core video accelerator clock - - "iface" Video accelerator AHB clock - - "bus" Video accelerator AXI clock - - "mbus" Video MAXI clock -- power-domains: - Usage: required - Value type: - Definition: A phandle and power domain specifier pairs to the - power domain which is responsible for collapsing - and restoring power to the peripheral. -- iommus: - Usage: required - Value type: - Definition: A list of phandle and IOMMU specifier pairs. -- memory-region: - Usage: required - Value type: - Definition: reference to the reserved-memory for the firmware - memory region. - -* Subnodes -The Venus video-codec node must contain two subnodes representing -video-decoder and video-encoder, and one optional firmware subnode. -Firmware subnode is needed when the platform does not have TrustZone. - -Every of video-encoder or video-decoder subnode should have: - -- compatible: - Usage: required - Value type: - Definition: Value should contain "venus-decoder" or "venus-encoder" -- clocks: - Usage: required for msm8996 - Value type: - Definition: A List of phandle and clock specifier pairs as listed - in clock-names property. -- clock-names: - Usage: required for msm8996 - Value type: - Definition: Should contain the following entries: - - "core" Subcore video accelerator clock - -- power-domains: - Usage: required for msm8996 - Value type: - Definition: A phandle and power domain specifier pairs to the - power domain which is responsible for collapsing - and restoring power to the subcore. - -The firmware subnode must have: - -- iommus: - Usage: required - Value type: - Definition: A list of phandle and IOMMU specifier pairs. - -* An Example - video-codec@1d00000 { - compatible = "qcom,msm8916-venus"; - reg = <0x01d00000 0xff000>; - interrupts = ; - clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, - <&gcc GCC_VENUS0_AHB_CLK>, - <&gcc GCC_VENUS0_AXI_CLK>; - clock-names = "core", "iface", "bus"; - power-domains = <&gcc VENUS_GDSC>; - iommus = <&apps_iommu 5>; - memory-region = <&venus_mem>; - - video-decoder { - compatible = "venus-decoder"; - clocks = <&mmcc VIDEO_SUBCORE0_CLK>; - clock-names = "core"; - power-domains = <&mmcc VENUS_CORE0_GDSC>; - }; - - video-encoder { - compatible = "venus-encoder"; - clocks = <&mmcc VIDEO_SUBCORE1_CLK>; - clock-names = "core"; - power-domains = <&mmcc VENUS_CORE1_GDSC>; - }; - - video-firmware { - iommus = <&apps_iommu 0x10b2 0x0>; - }; - }; From ea93e2f94c277890eca513d35897e61589ecc81a Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Tue, 14 Jan 2020 13:53:35 +0100 Subject: [PATCH 0687/2231] media: venus: core: add sc7180 DT compatible and resource struct This add DT compatible string and resource structure for sc7180. Signed-off-by: Dikshita Agarwal Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/core.c | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index c7525d951e92..194b10b98767 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -521,11 +521,56 @@ static const struct venus_resources sdm845_res_v2 = { .fwname = "qcom/venus-5.2/venus.mdt", }; +static const struct freq_tbl sc7180_freq_table[] = { + { 0, 500000000 }, + { 0, 434000000 }, + { 0, 340000000 }, + { 0, 270000000 }, + { 0, 150000000 }, +}; + +static const struct bw_tbl sc7180_bw_table_enc[] = { + { 972000, 750000, 0, 0, 0 }, /* 3840x2160@30 */ + { 489600, 451000, 0, 0, 0 }, /* 1920x1080@60 */ + { 244800, 234000, 0, 0, 0 }, /* 1920x1080@30 */ +}; + +static const struct bw_tbl sc7180_bw_table_dec[] = { + { 1036800, 1386000, 0, 1875000, 0 }, /* 4096x2160@30 */ + { 489600, 865000, 0, 1146000, 0 }, /* 1920x1080@60 */ + { 244800, 530000, 0, 583000, 0 }, /* 1920x1080@30 */ +}; + +static const struct venus_resources sc7180_res = { + .freq_tbl = sc7180_freq_table, + .freq_tbl_size = ARRAY_SIZE(sc7180_freq_table), + .bw_tbl_enc = sc7180_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sc7180_bw_table_enc), + .bw_tbl_dec = sc7180_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sc7180_bw_table_dec), + .codec_freq_data = sdm845_codec_freq_data, + .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), + .clks = {"core", "iface", "bus" }, + .clks_num = 3, + .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = { "venus", "vcodec0" }, + .vcodec_pmdomains_num = 2, + .vcodec_num = 1, + .hfi_version = HFI_VERSION_4XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/venus-5.4/venus.mdt", +}; + static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, + { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, { } }; MODULE_DEVICE_TABLE(of, venus_dt_match); From 545d984cc90c5843ffa61afb1d0e64302368c411 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Tue, 14 Jan 2020 13:53:34 +0100 Subject: [PATCH 0688/2231] media: dt-bindings: media: venus: Add sc7180 DT schema Add new qcom,sc7180-venus DT binding schema. Reviewed-by: Rob Herring Signed-off-by: Dikshita Agarwal Signed-off-by: Stanimir Varbanov Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/qcom,sc7180-venus.yaml | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml new file mode 100644 index 000000000000..764affa4877e --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,sc7180-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,sc7180-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 2 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + + clocks: + maxItems: 5 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: vcodec0_core + - const: vcodec0_bus + + iommus: + maxItems: 1 + + memory-region: + maxItems: 1 + + interconnects: + maxItems: 2 + + interconnect-names: + items: + - const: video-mem + - const: cpu-cfg + + video-decoder: + type: object + + properties: + compatible: + const: venus-decoder + + required: + - compatible + + additionalProperties: false + + video-encoder: + type: object + + properties: + compatible: + const: venus-encoder + + required: + - compatible + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - power-domain-names + - clocks + - clock-names + - iommus + - memory-region + - video-decoder + - video-encoder + +examples: + - | + #include + #include + + venus: video-codec@aa00000 { + compatible = "qcom,sc7180-venus"; + reg = <0 0x0aa00000 0 0xff000>; + interrupts = ; + power-domains = <&videocc VENUS_GDSC>, + <&videocc VCODEC0_GDSC>; + power-domain-names = "venus", "vcodec0"; + clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, + <&videocc VIDEO_CC_VENUS_AHB_CLK>, + <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, + <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC0_AXI_CLK>; + clock-names = "core", "iface", "bus", + "vcodec0_core", "vcodec0_bus"; + iommus = <&apps_smmu 0x0c00 0x60>; + memory-region = <&venus_mem>; + + video-decoder { + compatible = "venus-decoder"; + }; + + video-encoder { + compatible = "venus-encoder"; + }; + }; From 6c871b7314dde9ab64f20de8f5aa3d01be4518e8 Mon Sep 17 00:00:00 2001 From: Vasily Averin Date: Tue, 25 Feb 2020 11:11:20 +0300 Subject: [PATCH 0689/2231] pstore: pstore_ftrace_seq_next should increase position index In Aug 2018 NeilBrown noticed commit 1f4aace60b0e ("fs/seq_file.c: simplify seq_file iteration code and interface") "Some ->next functions do not increment *pos when they return NULL... Note that such ->next functions are buggy and should be fixed. A simple demonstration is dd if=/proc/swaps bs=1000 skip=1 Choose any block size larger than the size of /proc/swaps. This will always show the whole last line of /proc/swaps" /proc/swaps output was fixed recently, however there are lot of other affected files, and one of them is related to pstore subsystem. If .next function does not change position index, following .show function will repeat output related to current position index. There are at least 2 related problems: - read after lseek beyond end of file, described above by NeilBrown "dd if= bs=1000 skip=1" will generate whole last list - read after lseek on in middle of last line will output expected rest of last line but then repeat whole last line once again. If .show() function generates multy-line output (like pstore_ftrace_seq_show() does ?) following bash script cycles endlessly $ q=;while read -r r;do echo "$((++q)) $r";done < AFFECTED_FILE Unfortunately I'm not familiar enough to pstore subsystem and was unable to find affected pstore-related file on my test node. If .next function does not change position index, following .show function will repeat output related to current position index. Cc: stable@vger.kernel.org Fixes: 1f4aace60b0e ("fs/seq_file.c: simplify seq_file iteration code ...") Link: https://bugzilla.kernel.org/show_bug.cgi?id=206283 Signed-off-by: Vasily Averin Link: https://lore.kernel.org/r/4e49830d-4c88-0171-ee24-1ee540028dad@virtuozzo.com [kees: with robustness tweak from Joel Fernandes ] Signed-off-by: Kees Cook --- fs/pstore/inode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 7fbe8f058220..d99b5d39aa90 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -87,11 +87,11 @@ static void *pstore_ftrace_seq_next(struct seq_file *s, void *v, loff_t *pos) struct pstore_private *ps = s->private; struct pstore_ftrace_seq_data *data = v; + (*pos)++; data->off += REC_SIZE; if (data->off + REC_SIZE > ps->total_size) return NULL; - (*pos)++; return data; } @@ -101,6 +101,9 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v) struct pstore_ftrace_seq_data *data = v; struct pstore_ftrace_record *rec; + if (!data) + return 0; + rec = (struct pstore_ftrace_record *)(ps->record->buf + data->off); seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %ps <- %pS\n", From e8882e1bf3757224b031a8852078ea0a559fb864 Mon Sep 17 00:00:00 2001 From: Shawn Tu Date: Mon, 13 Jan 2020 03:27:02 -0300 Subject: [PATCH 0690/2231] media: ov5675: add vflip/hflip control support - Add V4L2 controls: horizontal/vertical flip, keep SGRBG10 Bayer order output (via change v/hflip) - Fix Bayer order output in 1296x972 binning registers Signed-off-by: Shawn Tu Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov5675.c | 82 +++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 1ae252378799..8537cc4ca108 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -63,6 +63,10 @@ #define OV5675_TEST_PATTERN_ENABLE BIT(7) #define OV5675_TEST_PATTERN_BAR_SHIFT 2 +/* Flip Mirror Controls from sensor */ +#define OV5675_REG_FORMAT1 0x3820 +#define OV5675_REG_FORMAT2 0x373d + #define to_ov5675(_sd) container_of(_sd, struct ov5675, sd) enum { @@ -314,21 +318,21 @@ static const struct ov5675_reg mode_1296x972_regs[] = { {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, - {0x3803, 0xf4}, + {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x3f}, - {0x3806, 0x06}, - {0x3807, 0xb3}, + {0x3806, 0x07}, + {0x3807, 0xb7}, {0x3808, 0x05}, - {0x3809, 0x00}, - {0x380a, 0x02}, - {0x380b, 0xd0}, + {0x3809, 0x10}, + {0x380a, 0x03}, + {0x380b, 0xcc}, {0x380c, 0x02}, {0x380d, 0xee}, {0x380e, 0x07}, - {0x380f, 0xe4}, - {0x3811, 0x10}, - {0x3813, 0x09}, + {0x380f, 0xd0}, + {0x3811, 0x08}, + {0x3813, 0x0d}, {0x3814, 0x03}, {0x3815, 0x01}, {0x3816, 0x03}, @@ -604,6 +608,53 @@ static int ov5675_test_pattern(struct ov5675 *ov5675, u32 pattern) OV5675_REG_VALUE_08BIT, pattern); } +/* + * OV5675 supports keeping the pixel order by mirror and flip function + * The Bayer order isn't affected by the flip controls + */ +static int ov5675_set_ctrl_hflip(struct ov5675 *ov5675, u32 ctrl_val) +{ + int ret; + u32 val; + + ret = ov5675_read_reg(ov5675, OV5675_REG_FORMAT1, + OV5675_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + return ov5675_write_reg(ov5675, OV5675_REG_FORMAT1, + OV5675_REG_VALUE_08BIT, + ctrl_val ? val & ~BIT(3) : val); +} + +static int ov5675_set_ctrl_vflip(struct ov5675 *ov5675, u8 ctrl_val) +{ + int ret; + u32 val; + + ret = ov5675_read_reg(ov5675, OV5675_REG_FORMAT1, + OV5675_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + ret = ov5675_write_reg(ov5675, OV5675_REG_FORMAT1, + OV5675_REG_VALUE_08BIT, + ctrl_val ? val | BIT(4) | BIT(5) : val); + + if (ret) + return ret; + + ret = ov5675_read_reg(ov5675, OV5675_REG_FORMAT2, + OV5675_REG_VALUE_08BIT, &val); + + if (ret) + return ret; + + return ov5675_write_reg(ov5675, OV5675_REG_FORMAT2, + OV5675_REG_VALUE_08BIT, + ctrl_val ? val | BIT(1) : val); +} + static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov5675 *ov5675 = container_of(ctrl->handler, @@ -654,6 +705,14 @@ static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) ret = ov5675_test_pattern(ov5675, ctrl->val); break; + case V4L2_CID_HFLIP: + ov5675_set_ctrl_hflip(ov5675, ctrl->val); + break; + + case V4L2_CID_VFLIP: + ov5675_set_ctrl_vflip(ov5675, ctrl->val); + break; + default: ret = -EINVAL; break; @@ -722,6 +781,11 @@ static int ov5675_init_controls(struct ov5675 *ov5675) V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov5675_test_pattern_menu) - 1, 0, 0, ov5675_test_pattern_menu); + v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ctrl_hdlr->error) return ctrl_hdlr->error; From a52dede4761885fe292d46e7b66f2755bd5b5430 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Mon, 20 Jan 2020 04:22:21 -0300 Subject: [PATCH 0691/2231] media: ipu3.rst: add imgu pipe config tool link A specific tool which can be used to generate imgu intermedia resolutions now is public on github, this patch adds this information into ipu3.rst. Signed-off-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/v4l-drivers/ipu3.rst | 11 +++++++---- drivers/staging/media/ipu3/TODO | 2 -- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst index e4904ab44e60..a694f49491f9 100644 --- a/Documentation/media/v4l-drivers/ipu3.rst +++ b/Documentation/media/v4l-drivers/ipu3.rst @@ -311,10 +311,13 @@ Down Scaler and GDC blocks should be configured with the supported resolutions as each hardware block has its own alignment requirement. You must configure the output resolution of the hardware blocks smartly to meet -the hardware requirement along with keeping the maximum field of view. -The intermediate resolutions can be generated by specific tool and this -information can be obtained by looking at the following IPU3 ImgU configuration -table. +the hardware requirement along with keeping the maximum field of view. The +intermediate resolutions can be generated by specific tool - + +https://github.com/intel/intel-ipu3-pipecfg + +This tool can be used to generate intermediate resolutions. More information can +be obtained by looking at the following IPU3 ImgU configuration table. https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/master diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO index dc356d6f03c4..52063b651810 100644 --- a/drivers/staging/media/ipu3/TODO +++ b/drivers/staging/media/ipu3/TODO @@ -13,8 +13,6 @@ staging directory. - Elaborate the functionality of different selection rectangles in driver documentation. This may require driver changes as well. -- More detailed documentation on calculating BDS, GCD etc. sizes needed. - - Document different operation modes, and which buffer queues are relevant in each mode. To process an image, which queues require a buffer an in which ones is it optional? From 9d730f2cf4c0391785855dd231577d2de2594df9 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Mon, 20 Jan 2020 05:15:57 -0300 Subject: [PATCH 0692/2231] media: dt-bindings: media: i2c: Add IMX219 CMOS sensor binding Add YAML device tree binding for IMX219 CMOS image sensor, and the relevant MAINTAINERS entries. Signed-off-by: Andrey Konovalov Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/i2c/imx219.yaml | 114 ++++++++++++++++++ MAINTAINERS | 8 ++ 2 files changed, 122 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/imx219.yaml b/Documentation/devicetree/bindings/media/i2c/imx219.yaml new file mode 100644 index 000000000000..32d6b693274f --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/imx219.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor + +maintainers: + - Dave Stevenson + +description: |- + The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor + with an active array size of 3280H x 2464V. It is programmable through + I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet. + Image data is sent through MIPI CSI-2, which is configured as either 2 or + 4 data lanes. + +properties: + compatible: + const: sony,imx219 + + reg: + description: I2C device address + maxItems: 1 + + clocks: + maxItems: 1 + + VDIG-supply: + description: + Digital I/O voltage supply, 1.8 volts + + VANA-supply: + description: + Analog voltage supply, 2.8 volts + + VDDL-supply: + description: + Digital core voltage supply, 1.2 volts + + reset-gpios: + description: |- + Reference to the GPIO connected to the xclr pin, if any. + Must be released (set high) after all supplies are applied. + + # See ../video-interfaces.txt for more details + port: + type: object + properties: + endpoint: + type: object + properties: + data-lanes: + description: |- + The sensor supports either two-lane, or four-lane operation. + If this property is omitted four-lane operation is assumed. + For two-lane operation the property must be set to <1 2>. + items: + - const: 1 + - const: 2 + + clock-noncontinuous: + type: boolean + description: |- + MIPI CSI-2 clock is non-continuous if this property is present, + otherwise it's continuous. + + link-frequencies: + allOf: + - $ref: /schemas/types.yaml#/definitions/uint64-array + description: + Allowed data bus frequencies. + + required: + - link-frequencies + +required: + - compatible + - reg + - clocks + - VANA-supply + - VDIG-supply + - VDDL-supply + - port + +additionalProperties: false + +examples: + - | + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + imx219: sensor@10 { + compatible = "sony,imx219"; + reg = <0x10>; + clocks = <&imx219_clk>; + VANA-supply = <&imx219_vana>; /* 2.8v */ + VDIG-supply = <&imx219_vdig>; /* 1.8v */ + VDDL-supply = <&imx219_vddl>; /* 1.2v */ + + port { + imx219_0: endpoint { + remote-endpoint = <&csi1_ep>; + data-lanes = <1 2>; + clock-noncontinuous; + link-frequencies = /bits/ 64 <456000000>; + }; + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index 9d45b59f33ed..6969dafac023 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15518,6 +15518,14 @@ S: Maintained F: drivers/media/i2c/imx214.c F: Documentation/devicetree/bindings/media/i2c/sony,imx214.txt +SONY IMX219 SENSOR DRIVER +M: Dave Stevenson +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/imx219.c +F: Documentation/devicetree/bindings/media/i2c/imx219.yaml + SONY IMX258 SENSOR DRIVER M: Sakari Ailus L: linux-media@vger.kernel.org From 1283b3b8f82b9004fbb94398cade5c8e797a2c8d Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 20 Jan 2020 05:15:58 -0300 Subject: [PATCH 0693/2231] media: i2c: Add driver for Sony IMX219 sensor Adds a driver for the 8MPix Sony IMX219 CSI2 sensor. Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver currently only supports 2 lanes. 8MPix @ 15fps, 1080P @ 30fps (cropped FOV), and 1640x1232 (2x2 binned) @ 30fps are currently supported. [Sakari Ailus: make imx219_check_hwcfg static] Signed-off-by: Dave Stevenson Signed-off-by: Andrey Konovalov Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx219.c | 1312 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1324 insertions(+) create mode 100644 drivers/media/i2c/imx219.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c68e002d26ea..87fba68542b5 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -591,6 +591,17 @@ config VIDEO_IMX214 To compile this driver as a module, choose M here: the module will be called imx214. +config VIDEO_IMX219 + tristate "Sony IMX219 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX219 camera. + + To compile this driver as a module, choose M here: the + module will be called imx219. + config VIDEO_IMX258 tristate "Sony IMX258 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index c147bb9d28db..77bf7d0b691f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -111,6 +111,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o +obj-$(CONFIG_VIDEO_IMX219) += imx219.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c new file mode 100644 index 000000000000..f1effb5a5f66 --- /dev/null +++ b/drivers/media/i2c/imx219.c @@ -0,0 +1,1312 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Sony IMX219 cameras. + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + * + * Based on Sony imx258 camera driver + * Copyright (C) 2018 Intel Corporation + * + * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver + * Copyright 2018 Qtechnology A/S + * + * Flip handling taken from the Sony IMX319 driver. + * Copyright (C) 2018 Intel Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX219_REG_VALUE_08BIT 1 +#define IMX219_REG_VALUE_16BIT 2 + +#define IMX219_REG_MODE_SELECT 0x0100 +#define IMX219_MODE_STANDBY 0x00 +#define IMX219_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX219_REG_CHIP_ID 0x0000 +#define IMX219_CHIP_ID 0x0219 + +/* External clock frequency is 24.0M */ +#define IMX219_XCLK_FREQ 24000000 + +/* Pixel rate is fixed at 182.4M for all the modes */ +#define IMX219_PIXEL_RATE 182400000 + +#define IMX219_DEFAULT_LINK_FREQ 456000000 + +/* V_TIMING internal */ +#define IMX219_REG_VTS 0x0160 +#define IMX219_VTS_15FPS 0x0dc6 +#define IMX219_VTS_30FPS_1080P 0x06e3 +#define IMX219_VTS_30FPS_BINNED 0x06e3 +#define IMX219_VTS_MAX 0xffff + +#define IMX219_VBLANK_MIN 4 + +/*Frame Length Line*/ +#define IMX219_FLL_MIN 0x08a6 +#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_STEP 1 +#define IMX219_FLL_DEFAULT 0x0c98 + +/* HBLANK control - read only */ +#define IMX219_PPL_DEFAULT 3448 + +/* Exposure control */ +#define IMX219_REG_EXPOSURE 0x015a +#define IMX219_EXPOSURE_MIN 4 +#define IMX219_EXPOSURE_STEP 1 +#define IMX219_EXPOSURE_DEFAULT 0x640 +#define IMX219_EXPOSURE_MAX 65535 + +/* Analog gain control */ +#define IMX219_REG_ANALOG_GAIN 0x0157 +#define IMX219_ANA_GAIN_MIN 0 +#define IMX219_ANA_GAIN_MAX 232 +#define IMX219_ANA_GAIN_STEP 1 +#define IMX219_ANA_GAIN_DEFAULT 0x0 + +/* Digital gain control */ +#define IMX219_REG_DIGITAL_GAIN 0x0158 +#define IMX219_DGTL_GAIN_MIN 0x0100 +#define IMX219_DGTL_GAIN_MAX 0x0fff +#define IMX219_DGTL_GAIN_DEFAULT 0x0100 +#define IMX219_DGTL_GAIN_STEP 1 + +#define IMX219_REG_ORIENTATION 0x0172 + +/* Test Pattern Control */ +#define IMX219_REG_TEST_PATTERN 0x0600 +#define IMX219_TEST_PATTERN_DISABLE 0 +#define IMX219_TEST_PATTERN_SOLID_COLOR 1 +#define IMX219_TEST_PATTERN_COLOR_BARS 2 +#define IMX219_TEST_PATTERN_GREY_COLOR 3 +#define IMX219_TEST_PATTERN_PN9 4 + +/* Test pattern colour components */ +#define IMX219_REG_TESTP_RED 0x0602 +#define IMX219_REG_TESTP_GREENR 0x0604 +#define IMX219_REG_TESTP_BLUE 0x0606 +#define IMX219_REG_TESTP_GREENB 0x0608 +#define IMX219_TESTP_COLOUR_MIN 0 +#define IMX219_TESTP_COLOUR_MAX 0x03ff +#define IMX219_TESTP_COLOUR_STEP 1 +#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX +#define IMX219_TESTP_GREENR_DEFAULT 0 +#define IMX219_TESTP_BLUE_DEFAULT 0 +#define IMX219_TESTP_GREENB_DEFAULT 0 + +struct imx219_reg { + u16 address; + u8 val; +}; + +struct imx219_reg_list { + unsigned int num_of_regs; + const struct imx219_reg *regs; +}; + +/* Mode : resolution and related config&values */ +struct imx219_mode { + /* Frame width */ + unsigned int width; + /* Frame height */ + unsigned int height; + + /* V-timing */ + unsigned int vts_def; + + /* Default register values */ + struct imx219_reg_list reg_list; +}; + +/* + * Register sets lifted off the i2C interface from the Raspberry Pi firmware + * driver. + * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4. + */ +static const struct imx219_reg mode_3280x2464_regs[] = { + {0x0100, 0x00}, + {0x30eb, 0x0c}, + {0x30eb, 0x05}, + {0x300a, 0xff}, + {0x300b, 0xff}, + {0x30eb, 0x05}, + {0x30eb, 0x09}, + {0x0114, 0x01}, + {0x0128, 0x00}, + {0x012a, 0x18}, + {0x012b, 0x00}, + {0x0164, 0x00}, + {0x0165, 0x00}, + {0x0166, 0x0c}, + {0x0167, 0xcf}, + {0x0168, 0x00}, + {0x0169, 0x00}, + {0x016a, 0x09}, + {0x016b, 0x9f}, + {0x016c, 0x0c}, + {0x016d, 0xd0}, + {0x016e, 0x09}, + {0x016f, 0xa0}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x00}, + {0x0175, 0x00}, + {0x018c, 0x0a}, + {0x018d, 0x0a}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x0309, 0x0a}, + {0x030b, 0x01}, + {0x030c, 0x00}, + {0x030d, 0x72}, + {0x0624, 0x0c}, + {0x0625, 0xd0}, + {0x0626, 0x09}, + {0x0627, 0xa0}, + {0x455e, 0x00}, + {0x471e, 0x4b}, + {0x4767, 0x0f}, + {0x4750, 0x14}, + {0x4540, 0x00}, + {0x47b4, 0x14}, + {0x4713, 0x30}, + {0x478b, 0x10}, + {0x478f, 0x10}, + {0x4793, 0x10}, + {0x4797, 0x0e}, + {0x479b, 0x0e}, + {0x0162, 0x0d}, + {0x0163, 0x78}, +}; + +static const struct imx219_reg mode_1920_1080_regs[] = { + {0x0100, 0x00}, + {0x30eb, 0x05}, + {0x30eb, 0x0c}, + {0x300a, 0xff}, + {0x300b, 0xff}, + {0x30eb, 0x05}, + {0x30eb, 0x09}, + {0x0114, 0x01}, + {0x0128, 0x00}, + {0x012a, 0x18}, + {0x012b, 0x00}, + {0x0162, 0x0d}, + {0x0163, 0x78}, + {0x0164, 0x02}, + {0x0165, 0xa8}, + {0x0166, 0x0a}, + {0x0167, 0x27}, + {0x0168, 0x02}, + {0x0169, 0xb4}, + {0x016a, 0x06}, + {0x016b, 0xeb}, + {0x016c, 0x07}, + {0x016d, 0x80}, + {0x016e, 0x04}, + {0x016f, 0x38}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x00}, + {0x0175, 0x00}, + {0x018c, 0x0a}, + {0x018d, 0x0a}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x0309, 0x0a}, + {0x030b, 0x01}, + {0x030c, 0x00}, + {0x030d, 0x72}, + {0x0624, 0x07}, + {0x0625, 0x80}, + {0x0626, 0x04}, + {0x0627, 0x38}, + {0x455e, 0x00}, + {0x471e, 0x4b}, + {0x4767, 0x0f}, + {0x4750, 0x14}, + {0x4540, 0x00}, + {0x47b4, 0x14}, + {0x4713, 0x30}, + {0x478b, 0x10}, + {0x478f, 0x10}, + {0x4793, 0x10}, + {0x4797, 0x0e}, + {0x479b, 0x0e}, + {0x0162, 0x0d}, + {0x0163, 0x78}, +}; + +static const struct imx219_reg mode_1640_1232_regs[] = { + {0x0100, 0x00}, + {0x30eb, 0x0c}, + {0x30eb, 0x05}, + {0x300a, 0xff}, + {0x300b, 0xff}, + {0x30eb, 0x05}, + {0x30eb, 0x09}, + {0x0114, 0x01}, + {0x0128, 0x00}, + {0x012a, 0x18}, + {0x012b, 0x00}, + {0x0164, 0x00}, + {0x0165, 0x00}, + {0x0166, 0x0c}, + {0x0167, 0xcf}, + {0x0168, 0x00}, + {0x0169, 0x00}, + {0x016a, 0x09}, + {0x016b, 0x9f}, + {0x016c, 0x06}, + {0x016d, 0x68}, + {0x016e, 0x04}, + {0x016f, 0xd0}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x01}, + {0x0175, 0x01}, + {0x018c, 0x0a}, + {0x018d, 0x0a}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x0309, 0x0a}, + {0x030b, 0x01}, + {0x030c, 0x00}, + {0x030d, 0x72}, + {0x0624, 0x06}, + {0x0625, 0x68}, + {0x0626, 0x04}, + {0x0627, 0xd0}, + {0x455e, 0x00}, + {0x471e, 0x4b}, + {0x4767, 0x0f}, + {0x4750, 0x14}, + {0x4540, 0x00}, + {0x47b4, 0x14}, + {0x4713, 0x30}, + {0x478b, 0x10}, + {0x478f, 0x10}, + {0x4793, 0x10}, + {0x4797, 0x0e}, + {0x479b, 0x0e}, + {0x0162, 0x0d}, + {0x0163, 0x78}, +}; + +static const char * const imx219_test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Solid Color", + "Grey Color Bars", + "PN9" +}; + +static const int imx219_test_pattern_val[] = { + IMX219_TEST_PATTERN_DISABLE, + IMX219_TEST_PATTERN_COLOR_BARS, + IMX219_TEST_PATTERN_SOLID_COLOR, + IMX219_TEST_PATTERN_GREY_COLOR, + IMX219_TEST_PATTERN_PN9, +}; + +/* regulator supplies */ +static const char * const imx219_supply_name[] = { + /* Supplies can be enabled in any order */ + "VANA", /* Analog (2.8V) supply */ + "VDIG", /* Digital Core (1.8V) supply */ + "VDDL", /* IF (1.2V) supply */ +}; + +#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name) + +/* + * Initialisation delay between XCLR low->high and the moment when the sensor + * can start capture (i.e. can leave software stanby) must be not less than: + * t4 + max(t5, t6 +