diff --git a/.gitignore b/.gitignore index 2a888b23d..59344a899 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,2 @@ -clog -*.xz -*.bz2 -*.rpm -*.orig -kernel-[234].*/ -perf-man-*.tar.gz +/_tmp/ +/.build-*.log diff --git a/0001-HID-input-ignore-System-Control-application-usages-i.patch b/0001-HID-input-ignore-System-Control-application-usages-i.patch new file mode 100644 index 000000000..9920598c2 --- /dev/null +++ b/0001-HID-input-ignore-System-Control-application-usages-i.patch @@ -0,0 +1,75 @@ +From 1989dada7ce07848196991c9ebf25ff9c5f14d4e Mon Sep 17 00:00:00 2001 +From: Benjamin Tissoires +Date: Tue, 13 Sep 2016 11:52:37 +0200 +Subject: [PATCH] HID: input: ignore System Control application usages if not + System Controls + +Microsoft is reusing its report descriptor again and again, and part of it +looks like this: + +0x05, 0x01, // Usage Page (Generic Desktop) 299 +0x09, 0x80, // Usage (System Control) 301 +0xa1, 0x01, // Collection (Application) 303 +0x85, 0x03, // Report ID (3) 305 +0x19, 0x00, // Usage Minimum (0) 307 +0x29, 0xff, // Usage Maximum (255) 309 +0x15, 0x00, // Logical Minimum (0) 311 +0x26, 0xff, 0x00, // Logical Maximum (255) 313 +0x81, 0x00, // Input (Data,Arr,Abs) 316 +0xc0, // End Collection 318 + +While there is nothing wrong in term of processing, we do however blindly +map the full usage range (it's an array) from 0x00 to 0xff, which creates +some interesting axis, like ABS_X|Y, and a bunch of ABS_MISC + n. + +While libinput and other stacks don't care that much (we can detect them), +joydev is very happy and attaches itself to the mouse or keyboard. + +The problem is that joydev now handles the device as a joystick, but given +that we have a HID array, it sets all the ABS_* values to 0. And in its +world, 0 means -32767 (minimum value), which sends spurious events to games +(think Steam). + +It looks like hid-microsoft tries to tackle the very same problem with its +.report_fixup callback. But fixing the report descriptor is an endless task +and is quite obfuscated. + +So take the hammer, and decide that if the application is meant to be +System Control, any other usage not in the System Control range should +be ignored. + +Link: https://bugzilla.redhat.com/show_bug.cgi?id=1325354 +Link: https://bugzilla.kernel.org/show_bug.cgi?id=28912 +Link: https://github.com/ValveSoftware/steam-for-linux/issues/3384 +Link: https://bugzilla.redhat.com/show_bug.cgi?id=1325354 +Link: https://bugzilla.kernel.org/show_bug.cgi?id=37982 + +Signed-off-by: Benjamin Tissoires +Signed-off-by: Jiri Kosina +--- + drivers/hid/hid-input.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c +index bcfaf32..058919d 100644 +--- a/drivers/hid/hid-input.c ++++ b/drivers/hid/hid-input.c +@@ -604,6 +604,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel + break; + } + ++ /* ++ * Some lazy vendors declare 255 usages for System Control, ++ * leading to the creation of ABS_X|Y axis and too many others. ++ * It wouldn't be a problem if joydev doesn't consider the ++ * device as a joystick then. ++ */ ++ if (field->application == HID_GD_SYSTEM_CONTROL) ++ goto ignore; ++ + if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ + switch (usage->hid) { + case HID_GD_UP: usage->hat_dir = 1; break; +-- +2.7.4 + diff --git a/0001-OOM-detection-regressions-since-4.7.patch b/0001-OOM-detection-regressions-since-4.7.patch new file mode 100644 index 000000000..4616c7f87 --- /dev/null +++ b/0001-OOM-detection-regressions-since-4.7.patch @@ -0,0 +1,121 @@ +From a7f80308bac4013728e33e2bcb9b60eee78f60fb Mon Sep 17 00:00:00 2001 +From: Michal Hocko +Date: Mon, 22 Aug 2016 11:32:49 +0200 +Subject: [PATCH] OOM detection regressions since 4.7 + +Hi, +there have been multiple reports [1][2][3][4][5] about pre-mature OOM +killer invocations since 4.7 which contains oom detection rework. All of +them were for order-2 (kernel stack) alloaction requests failing because +of a high fragmentation and compaction failing to make any forward +progress. While investigating this we have found out that the compaction +just gives up too early. Vlastimil has been working on compaction +improvement for quite some time and his series [6] is already sitting +in mmotm tree. This already helps a lot because it drops some heuristics +which are more aimed at lower latencies for high orders rather than +reliability. Joonsoo has then identified further problem with too many +blocks being marked as unmovable [7] and Vlastimil has prepared a patch +on top of his series [8] which is also in the mmotm tree now. + +That being said, the regression is real and should be fixed for 4.7 +stable users. [6][8] was reported to help and ooms are no longer +reproducible. I know we are quite late (rc3) in 4.8 but I would vote +for mergeing those patches and have them in 4.8. For 4.7 I would go +with a partial revert of the detection rework for high order requests +(see patch below). This patch is really trivial. If those compaction +improvements are just too large for 4.8 then we can use the same patch +as for 4.7 stable for now and revert it in 4.9 after compaction changes +are merged. + +Thoughts? + +[1] http://lkml.kernel.org/r/20160731051121.GB307@x4 +[2] http://lkml.kernel.org/r/201608120901.41463.a.miskiewicz@gmail.com +[3] http://lkml.kernel.org/r/20160801192620.GD31957@dhcp22.suse.cz +[4] https://lists.opensuse.org/opensuse-kernel/2016-08/msg00021.html +[5] https://bugzilla.opensuse.org/show_bug.cgi?id=994066 +[6] http://lkml.kernel.org/r/20160810091226.6709-1-vbabka@suse.cz +[7] http://lkml.kernel.org/r/20160816031222.GC16913@js1304-P5Q-DELUXE +[8] http://lkml.kernel.org/r/f7a9ea9d-bb88-bfd6-e340-3a933559305a@suse.cz +--- + mm/page_alloc.c | 50 ++------------------------------------------------ + 1 file changed, 2 insertions(+), 48 deletions(-) + +diff --git a/mm/page_alloc.c b/mm/page_alloc.c +index 8b3e134..6e35419 100644 +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -3254,53 +3254,6 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, + return NULL; + } + +-static inline bool +-should_compact_retry(struct alloc_context *ac, int order, int alloc_flags, +- enum compact_result compact_result, enum migrate_mode *migrate_mode, +- int compaction_retries) +-{ +- int max_retries = MAX_COMPACT_RETRIES; +- +- if (!order) +- return false; +- +- /* +- * compaction considers all the zone as desperately out of memory +- * so it doesn't really make much sense to retry except when the +- * failure could be caused by weak migration mode. +- */ +- if (compaction_failed(compact_result)) { +- if (*migrate_mode == MIGRATE_ASYNC) { +- *migrate_mode = MIGRATE_SYNC_LIGHT; +- return true; +- } +- return false; +- } +- +- /* +- * make sure the compaction wasn't deferred or didn't bail out early +- * due to locks contention before we declare that we should give up. +- * But do not retry if the given zonelist is not suitable for +- * compaction. +- */ +- if (compaction_withdrawn(compact_result)) +- return compaction_zonelist_suitable(ac, order, alloc_flags); +- +- /* +- * !costly requests are much more important than __GFP_REPEAT +- * costly ones because they are de facto nofail and invoke OOM +- * killer to move on while costly can fail and users are ready +- * to cope with that. 1/4 retries is rather arbitrary but we +- * would need much more detailed feedback from compaction to +- * make a better decision. +- */ +- if (order > PAGE_ALLOC_COSTLY_ORDER) +- max_retries /= 4; +- if (compaction_retries <= max_retries) +- return true; +- +- return false; +-} + #else + static inline struct page * + __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, +@@ -3311,6 +3264,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, + return NULL; + } + ++#endif /* CONFIG_COMPACTION */ ++ + static inline bool + should_compact_retry(struct alloc_context *ac, unsigned int order, int alloc_flags, + enum compact_result compact_result, +@@ -3337,7 +3292,6 @@ should_compact_retry(struct alloc_context *ac, unsigned int order, int alloc_fla + } + return false; + } +-#endif /* CONFIG_COMPACTION */ + + /* Perform direct synchronous page reclaim */ + static int +-- +2.7.4 + diff --git a/0001-cpupower-Correct-return-type-of-cpu_power_is_cpu_onl.patch b/0001-cpupower-Correct-return-type-of-cpu_power_is_cpu_onl.patch deleted file mode 100644 index 05b8cf999..000000000 --- a/0001-cpupower-Correct-return-type-of-cpu_power_is_cpu_onl.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 9f692cbe4a01dd9e3c3e954ec6b59662b68f9ce4 Mon Sep 17 00:00:00 2001 -From: Laura Abbott -Date: Fri, 9 Sep 2016 10:19:02 -0700 -Subject: [PATCH] cpupower: Correct return type of cpu_power_is_cpu_online in - cpufreq -To: Thomas Renninger -Cc: linux-pm@vger.kernel.org -Cc: linux-kernel@vger.kernel.org - -When converting to a shared library in ac5a181d065d ("cpupower: Add -cpuidle parts into library"), cpu_freq_cpu_exists was converted to -cpupower_is_cpu_online. cpu_req_cpu_exists returned 0 on success and --ENOSYS on failure whereas cpupower_is_cpu_online returns 1 on success. -Check for the correct return value in cpufreq-set. - -See https://bugzilla.redhat.com/show_bug.cgi?id=1374212 - -Fixes: ac5a181d065d ("cpupower: Add cpuidle parts into library") -Reported-by: Julian Seward -Signed-off-by: Laura Abbott ---- - tools/power/cpupower/utils/cpufreq-set.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/tools/power/cpupower/utils/cpufreq-set.c b/tools/power/cpupower/utils/cpufreq-set.c -index b4bf769..8971d71 100644 ---- a/tools/power/cpupower/utils/cpufreq-set.c -+++ b/tools/power/cpupower/utils/cpufreq-set.c -@@ -296,7 +296,7 @@ int cmd_freq_set(int argc, char **argv) - struct cpufreq_affected_cpus *cpus; - - if (!bitmask_isbitset(cpus_chosen, cpu) || -- cpupower_is_cpu_online(cpu)) -+ cpupower_is_cpu_online(cpu) != 1) - continue; - - cpus = cpufreq_get_related_cpus(cpu); -@@ -316,7 +316,7 @@ int cmd_freq_set(int argc, char **argv) - cpu <= bitmask_last(cpus_chosen); cpu++) { - - if (!bitmask_isbitset(cpus_chosen, cpu) || -- cpupower_is_cpu_online(cpu)) -+ cpupower_is_cpu_online(cpu) != 1) - continue; - - if (cpupower_is_cpu_online(cpu) != 1) --- -2.10.0 - diff --git a/0001-drm-i915-Refresh-that-status-of-MST-capable-connecto.patch b/0001-drm-i915-Refresh-that-status-of-MST-capable-connecto.patch new file mode 100644 index 000000000..6e38648ac --- /dev/null +++ b/0001-drm-i915-Refresh-that-status-of-MST-capable-connecto.patch @@ -0,0 +1,61 @@ +From 1aab956c7b8872fb6976328316bfad62c6e67cf8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= +Date: Fri, 21 Oct 2016 16:44:38 +0300 +Subject: [PATCH] drm/i915: Refresh that status of MST capable connectors in + ->detect() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Once we've determined that the sink is MST capable we never end up +running through the full detect cycle again, despite getting HPDs. +Fix tht by ripping out the incorrect piece of code responsible. + +This got broken when I moved the long HPD handling to the ->detect() +hook, but failed to remove the leftover code. + +Cc: Ander Conselvan de Oliveira +Cc: drm-intel-fixes@lists.freedesktop.org +Cc: Rui Tiago Matos +Tested-by: Rui Tiago Matos +Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=98323 +Cc: Kirill A. Shutemov +Tested-by: Kirill A. Shutemov +References: https://bugs.freedesktop.org/show_bug.cgi?id=98306 +Fixes: 27d4efc5591a ("drm/i915: Move long hpd handling into the hotplug work") +Signed-off-by: Ville Syrjälä +Link: http://patchwork.freedesktop.org/patch/msgid/1477057478-29328-1-git-send-email-ville.syrjala@linux.intel.com +Reviewed-by: Chris Wilson +--- + drivers/gpu/drm/i915/intel_dp.c | 10 ---------- + 1 file changed, 10 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c +index f30db8f..80db8a3 100644 +--- a/drivers/gpu/drm/i915/intel_dp.c ++++ b/drivers/gpu/drm/i915/intel_dp.c +@@ -4492,21 +4492,11 @@ static enum drm_connector_status + intel_dp_detect(struct drm_connector *connector, bool force) + { + struct intel_dp *intel_dp = intel_attached_dp(connector); +- struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); +- struct intel_encoder *intel_encoder = &intel_dig_port->base; + enum drm_connector_status status = connector->status; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + +- if (intel_dp->is_mst) { +- /* MST devices are disconnected from a monitor POV */ +- intel_dp_unset_edid(intel_dp); +- if (intel_encoder->type != INTEL_OUTPUT_EDP) +- intel_encoder->type = INTEL_OUTPUT_DP; +- return connector_status_disconnected; +- } +- + /* If full detect is not performed yet, do a full detect */ + if (!intel_dp->detect_done) + status = intel_dp_long_pulse(intel_dp->attached_connector); +-- +2.7.4 + diff --git a/0001-drm-i915-Reorganize-WM-structs-unions-in-CRTC-state.patch b/0001-drm-i915-Reorganize-WM-structs-unions-in-CRTC-state.patch new file mode 100644 index 000000000..f30e32dc4 --- /dev/null +++ b/0001-drm-i915-Reorganize-WM-structs-unions-in-CRTC-state.patch @@ -0,0 +1,227 @@ +From 0042e1e7a03a2fb5d6c464c03ce84d55b31add11 Mon Sep 17 00:00:00 2001 +From: Matt Roper +Date: Thu, 12 May 2016 07:05:55 -0700 +Subject: [PATCH 01/17] drm/i915: Reorganize WM structs/unions in CRTC state + +Reorganize the nested structures and unions we have for pipe watermark +data in intel_crtc_state so that platform-specific data can be added in +a more sensible manner (and save a bit of memory at the same time). + +The change basically changes the organization from: + + union { + struct intel_pipe_wm ilk; + struct intel_pipe_wm skl; + } optimal; + + struct intel_pipe_wm intermediate /* ILK-only */ + +to + + union { + struct { + struct intel_pipe_wm intermediate; + struct intel_pipe_wm optimal; + } ilk; + + struct { + struct intel_pipe_wm optimal; + } skl; + } + +There should be no functional change here, but it will allow us to add +more platform-specific fields going forward (and more easily extend to +other platform types like VLV). + +While we're at it, let's move the entire watermark substructure out to +its own structure definition to make the code slightly more readable. + +Signed-off-by: Matt Roper +Reviewed-by: Maarten Lankhorst +Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-2-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_display.c | 2 +- + drivers/gpu/drm/i915/intel_drv.h | 61 +++++++++++++++++++++--------------- + drivers/gpu/drm/i915/intel_pm.c | 18 +++++------ + 3 files changed, 45 insertions(+), 36 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index d19b392..4633aec 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -12027,7 +12027,7 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc, + } + } else if (dev_priv->display.compute_intermediate_wm) { + if (HAS_PCH_SPLIT(dev_priv) && INTEL_GEN(dev_priv) < 9) +- pipe_config->wm.intermediate = pipe_config->wm.optimal.ilk; ++ pipe_config->wm.ilk.intermediate = pipe_config->wm.ilk.optimal; + } + + if (INTEL_INFO(dev)->gen >= 9) { +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index 4a24b00..5a186bf 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -405,6 +405,40 @@ struct skl_pipe_wm { + uint32_t linetime; + }; + ++struct intel_crtc_wm_state { ++ union { ++ struct { ++ /* ++ * Intermediate watermarks; these can be ++ * programmed immediately since they satisfy ++ * both the current configuration we're ++ * switching away from and the new ++ * configuration we're switching to. ++ */ ++ struct intel_pipe_wm intermediate; ++ ++ /* ++ * Optimal watermarks, programmed post-vblank ++ * when this state is committed. ++ */ ++ struct intel_pipe_wm optimal; ++ } ilk; ++ ++ struct { ++ /* gen9+ only needs 1-step wm programming */ ++ struct skl_pipe_wm optimal; ++ } skl; ++ }; ++ ++ /* ++ * Platforms with two-step watermark programming will need to ++ * update watermark programming post-vblank to switch from the ++ * safe intermediate watermarks to the optimal final ++ * watermarks. ++ */ ++ bool need_postvbl_update; ++}; ++ + struct intel_crtc_state { + struct drm_crtc_state base; + +@@ -558,32 +592,7 @@ struct intel_crtc_state { + /* IVB sprite scaling w/a (WaCxSRDisabledForSpriteScaling:ivb) */ + bool disable_lp_wm; + +- struct { +- /* +- * Optimal watermarks, programmed post-vblank when this state +- * is committed. +- */ +- union { +- struct intel_pipe_wm ilk; +- struct skl_pipe_wm skl; +- } optimal; +- +- /* +- * Intermediate watermarks; these can be programmed immediately +- * since they satisfy both the current configuration we're +- * switching away from and the new configuration we're switching +- * to. +- */ +- struct intel_pipe_wm intermediate; +- +- /* +- * Platforms with two-step watermark programming will need to +- * update watermark programming post-vblank to switch from the +- * safe intermediate watermarks to the optimal final +- * watermarks. +- */ +- bool need_postvbl_update; +- } wm; ++ struct intel_crtc_wm_state wm; + + /* Gamma mode programmed on the pipe */ + uint32_t gamma_mode; +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index a7ef45d..4353fec 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -2309,7 +2309,7 @@ static int ilk_compute_pipe_wm(struct intel_crtc_state *cstate) + int level, max_level = ilk_wm_max_level(dev), usable_level; + struct ilk_wm_maximums max; + +- pipe_wm = &cstate->wm.optimal.ilk; ++ pipe_wm = &cstate->wm.ilk.optimal; + + for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { + struct intel_plane_state *ps; +@@ -2391,7 +2391,7 @@ static int ilk_compute_intermediate_wm(struct drm_device *dev, + struct intel_crtc *intel_crtc, + struct intel_crtc_state *newstate) + { +- struct intel_pipe_wm *a = &newstate->wm.intermediate; ++ struct intel_pipe_wm *a = &newstate->wm.ilk.intermediate; + struct intel_pipe_wm *b = &intel_crtc->wm.active.ilk; + int level, max_level = ilk_wm_max_level(dev); + +@@ -2400,7 +2400,7 @@ static int ilk_compute_intermediate_wm(struct drm_device *dev, + * currently active watermarks to get values that are safe both before + * and after the vblank. + */ +- *a = newstate->wm.optimal.ilk; ++ *a = newstate->wm.ilk.optimal; + a->pipe_enabled |= b->pipe_enabled; + a->sprites_enabled |= b->sprites_enabled; + a->sprites_scaled |= b->sprites_scaled; +@@ -2429,7 +2429,7 @@ static int ilk_compute_intermediate_wm(struct drm_device *dev, + * If our intermediate WM are identical to the final WM, then we can + * omit the post-vblank programming; only update if it's different. + */ +- if (memcmp(a, &newstate->wm.optimal.ilk, sizeof(*a)) == 0) ++ if (memcmp(a, &newstate->wm.ilk.optimal, sizeof(*a)) == 0) + newstate->wm.need_postvbl_update = false; + + return 0; +@@ -3678,7 +3678,7 @@ static void skl_update_wm(struct drm_crtc *crtc) + struct drm_i915_private *dev_priv = dev->dev_private; + struct skl_wm_values *results = &dev_priv->wm.skl_results; + struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); +- struct skl_pipe_wm *pipe_wm = &cstate->wm.optimal.skl; ++ struct skl_pipe_wm *pipe_wm = &cstate->wm.skl.optimal; + + + /* Clear all dirty flags */ +@@ -3757,7 +3757,7 @@ static void ilk_initial_watermarks(struct intel_crtc_state *cstate) + struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc); + + mutex_lock(&dev_priv->wm.wm_mutex); +- intel_crtc->wm.active.ilk = cstate->wm.intermediate; ++ intel_crtc->wm.active.ilk = cstate->wm.ilk.intermediate; + ilk_program_watermarks(dev_priv); + mutex_unlock(&dev_priv->wm.wm_mutex); + } +@@ -3769,7 +3769,7 @@ static void ilk_optimize_watermarks(struct intel_crtc_state *cstate) + + mutex_lock(&dev_priv->wm.wm_mutex); + if (cstate->wm.need_postvbl_update) { +- intel_crtc->wm.active.ilk = cstate->wm.optimal.ilk; ++ intel_crtc->wm.active.ilk = cstate->wm.ilk.optimal; + ilk_program_watermarks(dev_priv); + } + mutex_unlock(&dev_priv->wm.wm_mutex); +@@ -3826,7 +3826,7 @@ static void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc) + struct skl_wm_values *hw = &dev_priv->wm.skl_hw; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); +- struct skl_pipe_wm *active = &cstate->wm.optimal.skl; ++ struct skl_pipe_wm *active = &cstate->wm.skl.optimal; + enum pipe pipe = intel_crtc->pipe; + int level, i, max_level; + uint32_t temp; +@@ -3892,7 +3892,7 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc) + struct ilk_wm_values *hw = &dev_priv->wm.hw; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); +- struct intel_pipe_wm *active = &cstate->wm.optimal.ilk; ++ struct intel_pipe_wm *active = &cstate->wm.ilk.optimal; + enum pipe pipe = intel_crtc->pipe; + static const i915_reg_t wm0_pipe_reg[] = { + [PIPE_A] = WM0_PIPEA_ILK, +-- +2.7.4 + diff --git a/0001-ipv6-Avoid-creating-RTF_CACHE-from-a-rt-that-is-not-.patch b/0001-ipv6-Avoid-creating-RTF_CACHE-from-a-rt-that-is-not-.patch new file mode 100644 index 000000000..3390024d2 --- /dev/null +++ b/0001-ipv6-Avoid-creating-RTF_CACHE-from-a-rt-that-is-not-.patch @@ -0,0 +1,91 @@ +From 0d3f6d297bfb7af24d0508460fdb3d1ec4903fa3 Mon Sep 17 00:00:00 2001 +From: Martin KaFai Lau +Date: Wed, 11 Nov 2015 11:51:06 -0800 +Subject: [PATCH] ipv6: Avoid creating RTF_CACHE from a rt that is not managed + by fib6 tree + +The original bug report: +https://bugzilla.redhat.com/show_bug.cgi?id=1272571 + +The setup has a IPv4 GRE tunnel running in a IPSec. The bug +happens when ndisc starts sending router solicitation at the gre +interface. The simplified oops stack is like: + +__lock_acquire+0x1b2/0x1c30 +lock_acquire+0xb9/0x140 +_raw_write_lock_bh+0x3f/0x50 +__ip6_ins_rt+0x2e/0x60 +ip6_ins_rt+0x49/0x50 +~~~~~~~~ +__ip6_rt_update_pmtu.part.54+0x145/0x250 +ip6_rt_update_pmtu+0x2e/0x40 +~~~~~~~~ +ip_tunnel_xmit+0x1f1/0xf40 +__gre_xmit+0x7a/0x90 +ipgre_xmit+0x15a/0x220 +dev_hard_start_xmit+0x2bd/0x480 +__dev_queue_xmit+0x696/0x730 +dev_queue_xmit+0x10/0x20 +neigh_direct_output+0x11/0x20 +ip6_finish_output2+0x21f/0x770 +ip6_finish_output+0xa7/0x1d0 +ip6_output+0x56/0x190 +~~~~~~~~ +ndisc_send_skb+0x1d9/0x400 +ndisc_send_rs+0x88/0xc0 +~~~~~~~~ + +The rt passed to ip6_rt_update_pmtu() is created by +icmp6_dst_alloc() and it is not managed by the fib6 tree, +so its rt6i_table == NULL. When __ip6_rt_update_pmtu() creates +a RTF_CACHE clone, the newly created clone also has rt6i_table == NULL +and it causes the ip6_ins_rt() oops. + +During pmtu update, we only want to create a RTF_CACHE clone +from a rt which is currently managed (or owned) by the +fib6 tree. It means either rt->rt6i_node != NULL or +rt is a RTF_PCPU clone. + +It is worth to note that rt6i_table may not be NULL even it is +not (yet) managed by the fib6 tree (e.g. addrconf_dst_alloc()). +Hence, rt6i_node is a better check instead of rt6i_table. + +Fixes: 45e4fd26683c ("ipv6: Only create RTF_CACHE routes after encountering pmtu") +Signed-off-by: Martin KaFai Lau +Reported-by: Chris Siebenmann +Cc: Chris Siebenmann +Cc: Hannes Frederic Sowa +Signed-off-by: David S. Miller +--- + net/ipv6/route.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index c8bc9b4..74907c5 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -1322,6 +1322,12 @@ static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) + rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); + } + ++static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) ++{ ++ return !(rt->rt6i_flags & RTF_CACHE) && ++ (rt->rt6i_flags & RTF_PCPU || rt->rt6i_node); ++} ++ + static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, + const struct ipv6hdr *iph, u32 mtu) + { +@@ -1335,7 +1341,7 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, + if (mtu >= dst_mtu(dst)) + return; + +- if (rt6->rt6i_flags & RTF_CACHE) { ++ if (!rt6_cache_allowed_for_pmtu(rt6)) { + rt6_do_update_pmtu(rt6, mtu); + } else { + const struct in6_addr *daddr, *saddr; +-- +2.5.0 + diff --git a/0001-net-dsa-mv88e6xxx-Introduce-_mv88e6xxx_phy_page_-rea.patch b/0001-net-dsa-mv88e6xxx-Introduce-_mv88e6xxx_phy_page_-rea.patch new file mode 100644 index 000000000..1e2d4db59 --- /dev/null +++ b/0001-net-dsa-mv88e6xxx-Introduce-_mv88e6xxx_phy_page_-rea.patch @@ -0,0 +1,95 @@ +From 4d1b08a69350d40e0aa14baba4797ef175295718 Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Wed, 30 Mar 2016 12:40:54 +0100 +Subject: [PATCH 1/2] net: dsa: mv88e6xxx: Introduce + _mv88e6xxx_phy_page_{read,write} + +Add versions of the phy_page_read and _write functions to +be used in a context where the SMI mutex is held. + +Tested-by: Vivien Didelot +Reviewed-by: Vivien Didelot +Signed-off-by: Patrick Uiterwijk +--- + drivers/net/dsa/mv88e6xxx.c | 49 +++++++++++++++++++++++++++++++++------------ + 1 file changed, 36 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c +index 512c8c0..3dcfe13 100644 +--- a/drivers/net/dsa/mv88e6xxx.c ++++ b/drivers/net/dsa/mv88e6xxx.c +@@ -1929,6 +1929,38 @@ static void mv88e6xxx_bridge_work(struct work_struct *work) + } + } + ++static int _mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, ++ int reg, int val) ++{ ++ int ret; ++ ++ ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); ++ if (ret < 0) ++ goto restore_page_0; ++ ++ ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val); ++restore_page_0: ++ _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); ++ ++ return ret; ++} ++ ++static int _mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, ++ int reg) ++{ ++ int ret; ++ ++ ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); ++ if (ret < 0) ++ goto restore_page_0; ++ ++ ret = _mv88e6xxx_phy_read_indirect(ds, port, reg); ++restore_page_0: ++ _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); ++ ++ return ret; ++} ++ + static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) + { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); +@@ -2383,13 +2415,9 @@ int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) + int ret; + + mutex_lock(&ps->smi_mutex); +- ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); +- if (ret < 0) +- goto error; +- ret = _mv88e6xxx_phy_read_indirect(ds, port, reg); +-error: +- _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); ++ ret = _mv88e6xxx_phy_page_read(ds, port, page, reg); + mutex_unlock(&ps->smi_mutex); ++ + return ret; + } + +@@ -2400,14 +2428,9 @@ int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, + int ret; + + mutex_lock(&ps->smi_mutex); +- ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); +- if (ret < 0) +- goto error; +- +- ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val); +-error: +- _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); ++ ret = _mv88e6xxx_phy_page_write(ds, port, page, reg, val); + mutex_unlock(&ps->smi_mutex); ++ + return ret; + } + +-- +2.7.3 + diff --git a/0001-platform-x86-ideapad-laptop-Add-Lenovo-Yoga-910-13IK.patch b/0001-platform-x86-ideapad-laptop-Add-Lenovo-Yoga-910-13IK.patch new file mode 100644 index 000000000..42e935206 --- /dev/null +++ b/0001-platform-x86-ideapad-laptop-Add-Lenovo-Yoga-910-13IK.patch @@ -0,0 +1,40 @@ +From 40c30bbf3377babc4d6bb16b699184236a8bfa27 Mon Sep 17 00:00:00 2001 +From: Brian Masney +Date: Tue, 11 Oct 2016 19:28:02 -0400 +Subject: [PATCH] platform/x86: ideapad-laptop: Add Lenovo Yoga 910-13IKB to + no_hw_rfkill dmi list + +The Lenovo Yoga 910-13IKB does not have a hw rfkill switch, and trying +to read the hw rfkill switch through the ideapad module causes it to +always report as blocked. + +This commit adds the Lenovo Yoga 910-13IKB to the no_hw_rfkill dmi list, +fixing the WiFI breakage. + +Signed-off-by: Brian Masney +Signed-off-by: Darren Hart +--- + drivers/platform/x86/ideapad-laptop.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c +index d1a091b..a232394 100644 +--- a/drivers/platform/x86/ideapad-laptop.c ++++ b/drivers/platform/x86/ideapad-laptop.c +@@ -933,6 +933,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 900"), + }, + }, ++ { ++ .ident = "Lenovo YOGA 910-13IKB", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"), ++ }, ++ }, + {} + }; + +-- +2.7.4 + diff --git a/0001-selinux-Only-apply-bounds-checking-to-source-types.patch b/0001-selinux-Only-apply-bounds-checking-to-source-types.patch new file mode 100644 index 000000000..dcc9e6b2b --- /dev/null +++ b/0001-selinux-Only-apply-bounds-checking-to-source-types.patch @@ -0,0 +1,203 @@ +From 7ea59202db8d20806d9ae552acd1875c3a978bcc Mon Sep 17 00:00:00 2001 +From: Stephen Smalley +Date: Mon, 23 May 2016 10:54:11 -0400 +Subject: [PATCH] selinux: Only apply bounds checking to source types + +The current bounds checking of both source and target types +requires allowing any domain that has access to the child +domain to also have the same permissions to the parent, which +is undesirable. Drop the target bounds checking. + +KaiGai Kohei originally removed all use of target bounds in +commit 7d52a155e38d ("selinux: remove dead code in +type_attribute_bounds_av()") but this was reverted in +commit 2ae3ba39389b ("selinux: libsepol: remove dead code in +check_avtab_hierarchy_callback()") because it would have +required explicitly allowing the parent any permissions +to the child that the child is allowed to itself. + +This change in contrast retains the logic for the case where both +source and target types are bounded, thereby allowing access +if the parent of the source is allowed the corresponding +permissions to the parent of the target. Further, this change +reworks the logic such that we only perform a single computation +for each case and there is no ambiguity as to how to resolve +a bounds violation. + +Under the new logic, if the source type and target types are both +bounded, then the parent of the source type must be allowed the same +permissions to the parent of the target type. If only the source +type is bounded, then the parent of the source type must be allowed +the same permissions to the target type. + +Examples of the new logic and comparisons with the old logic: +1. If we have: + typebounds A B; +then: + allow B self:process ; +will satisfy the bounds constraint iff: + allow A self:process ; +is also allowed in policy. + +Under the old logic, the allow rule on B satisfies the +bounds constraint if any of the following three are allowed: + allow A B:process ; or + allow B A:process ; or + allow A self:process ; +However, either of the first two ultimately require the third to +satisfy the bounds constraint under the old logic, and therefore +this degenerates to the same result (but is more efficient - we only +need to perform one compute_av call). + +2. If we have: + typebounds A B; + typebounds A_exec B_exec; +then: + allow B B_exec:file ; +will satisfy the bounds constraint iff: + allow A A_exec:file ; +is also allowed in policy. + +This is essentially the same as #1; it is merely included as +an example of dealing with object types related to a bounded domain +in a manner that satisfies the bounds relationship. Note that +this approach is preferable to leaving B_exec unbounded and having: + allow A B_exec:file ; +in policy because that would allow B's entrypoints to be used to +enter A. Similarly for _tmp or other related types. + +3. If we have: + typebounds A B; +and an unbounded type T, then: + allow B T:file ; +will satisfy the bounds constraint iff: + allow A T:file ; +is allowed in policy. + +The old logic would have been identical for this example. + +4. If we have: + typebounds A B; +and an unbounded domain D, then: + allow D B:unix_stream_socket ; +is not subject to any bounds constraints under the new logic +because D is not bounded. This is desirable so that we can +allow a domain to e.g. connectto a child domain without having +to allow it to do the same to its parent. + +The old logic would have required: + allow D A:unix_stream_socket ; +to also be allowed in policy. + +Signed-off-by: Stephen Smalley +[PM: re-wrapped description to appease checkpatch.pl] +Signed-off-by: Paul Moore +--- + security/selinux/ss/services.c | 70 +++++++++++++----------------------------- + 1 file changed, 22 insertions(+), 48 deletions(-) + +diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c +index 89df646..082b20c 100644 +--- a/security/selinux/ss/services.c ++++ b/security/selinux/ss/services.c +@@ -543,7 +543,7 @@ static void type_attribute_bounds_av(struct context *scontext, + struct av_decision *avd) + { + struct context lo_scontext; +- struct context lo_tcontext; ++ struct context lo_tcontext, *tcontextp = tcontext; + struct av_decision lo_avd; + struct type_datum *source; + struct type_datum *target; +@@ -553,67 +553,41 @@ static void type_attribute_bounds_av(struct context *scontext, + scontext->type - 1); + BUG_ON(!source); + ++ if (!source->bounds) ++ return; ++ + target = flex_array_get_ptr(policydb.type_val_to_struct_array, + tcontext->type - 1); + BUG_ON(!target); + +- if (source->bounds) { +- memset(&lo_avd, 0, sizeof(lo_avd)); +- +- memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); +- lo_scontext.type = source->bounds; ++ memset(&lo_avd, 0, sizeof(lo_avd)); + +- context_struct_compute_av(&lo_scontext, +- tcontext, +- tclass, +- &lo_avd, +- NULL); +- if ((lo_avd.allowed & avd->allowed) == avd->allowed) +- return; /* no masked permission */ +- masked = ~lo_avd.allowed & avd->allowed; +- } ++ memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); ++ lo_scontext.type = source->bounds; + + if (target->bounds) { +- memset(&lo_avd, 0, sizeof(lo_avd)); +- + memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); + lo_tcontext.type = target->bounds; +- +- context_struct_compute_av(scontext, +- &lo_tcontext, +- tclass, +- &lo_avd, +- NULL); +- if ((lo_avd.allowed & avd->allowed) == avd->allowed) +- return; /* no masked permission */ +- masked = ~lo_avd.allowed & avd->allowed; ++ tcontextp = &lo_tcontext; + } + +- if (source->bounds && target->bounds) { +- memset(&lo_avd, 0, sizeof(lo_avd)); +- /* +- * lo_scontext and lo_tcontext are already +- * set up. +- */ ++ context_struct_compute_av(&lo_scontext, ++ tcontextp, ++ tclass, ++ &lo_avd, ++ NULL); + +- context_struct_compute_av(&lo_scontext, +- &lo_tcontext, +- tclass, +- &lo_avd, +- NULL); +- if ((lo_avd.allowed & avd->allowed) == avd->allowed) +- return; /* no masked permission */ +- masked = ~lo_avd.allowed & avd->allowed; +- } ++ masked = ~lo_avd.allowed & avd->allowed; + +- if (masked) { +- /* mask violated permissions */ +- avd->allowed &= ~masked; ++ if (likely(!masked)) ++ return; /* no masked permission */ + +- /* audit masked permissions */ +- security_dump_masked_av(scontext, tcontext, +- tclass, masked, "bounds"); +- } ++ /* mask violated permissions */ ++ avd->allowed &= ~masked; ++ ++ /* audit masked permissions */ ++ security_dump_masked_av(scontext, tcontext, ++ tclass, masked, "bounds"); + } + + /* +-- +2.7.4 + diff --git a/0001-udp-fix-poll-issue-with-zero-sized-packets.patch b/0001-udp-fix-poll-issue-with-zero-sized-packets.patch new file mode 100644 index 000000000..f5edf2340 --- /dev/null +++ b/0001-udp-fix-poll-issue-with-zero-sized-packets.patch @@ -0,0 +1,73 @@ +From 9f30f83eb6347afa6b1d1df1065608c2b4485e2b Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Tue, 23 Aug 2016 13:59:33 -0700 +Subject: [PATCH] udp: fix poll() issue with zero sized packets + +Laura tracked poll() [and friends] regression caused by commit +e6afc8ace6dd ("udp: remove headers from UDP packets before queueing") + +udp_poll() needs to know if there is a valid packet in receive queue, +even if its payload length is 0. + +Change first_packet_length() to return an signed int, and use -1 +as the indication of an empty queue. + +Fixes: e6afc8ace6dd ("udp: remove headers from UDP packets before queueing") +Reported-by: Laura Abbott +Signed-off-by: Eric Dumazet +Tested-by: Laura Abbott +--- + net/ipv4/udp.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c +index e61f7cd..00d18c5 100644 +--- a/net/ipv4/udp.c ++++ b/net/ipv4/udp.c +@@ -1182,13 +1182,13 @@ out: + * @sk: socket + * + * Drops all bad checksum frames, until a valid one is found. +- * Returns the length of found skb, or 0 if none is found. ++ * Returns the length of found skb, or -1 if none is found. + */ +-static unsigned int first_packet_length(struct sock *sk) ++static int first_packet_length(struct sock *sk) + { + struct sk_buff_head list_kill, *rcvq = &sk->sk_receive_queue; + struct sk_buff *skb; +- unsigned int res; ++ int res; + + __skb_queue_head_init(&list_kill); + +@@ -1203,7 +1203,7 @@ static unsigned int first_packet_length(struct sock *sk) + __skb_unlink(skb, rcvq); + __skb_queue_tail(&list_kill, skb); + } +- res = skb ? skb->len : 0; ++ res = skb ? skb->len : -1; + spin_unlock_bh(&rcvq->lock); + + if (!skb_queue_empty(&list_kill)) { +@@ -1232,7 +1232,7 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) + + case SIOCINQ: + { +- unsigned int amount = first_packet_length(sk); ++ int amount = max_t(int, 0, first_packet_length(sk)); + + return put_user(amount, (int __user *)arg); + } +@@ -2184,7 +2184,7 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait) + + /* Check for false positives due to checksum errors */ + if ((mask & POLLRDNORM) && !(file->f_flags & O_NONBLOCK) && +- !(sk->sk_shutdown & RCV_SHUTDOWN) && !first_packet_length(sk)) ++ !(sk->sk_shutdown & RCV_SHUTDOWN) && first_packet_length(sk) == -1) + mask &= ~(POLLIN | POLLRDNORM); + + return mask; +-- +2.7.4 + diff --git a/0001-x86-cpu-cacheinfo-Fix-teardown-path.patch b/0001-x86-cpu-cacheinfo-Fix-teardown-path.patch new file mode 100644 index 000000000..52e82feed --- /dev/null +++ b/0001-x86-cpu-cacheinfo-Fix-teardown-path.patch @@ -0,0 +1,86 @@ +From 680ac028240f8747f31c03986fbcf18b2b521e93 Mon Sep 17 00:00:00 2001 +From: Borislav Petkov +Date: Mon, 27 Jul 2015 09:58:05 +0200 +Subject: [PATCH] x86/cpu/cacheinfo: Fix teardown path +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Philip Müller reported a hang when booting 32-bit 4.1 kernel on +an AMD box. A fragment of the splat was enough to pinpoint the +issue: + + task: f58e0000 ti: f58e8000 task.ti: f58e800 + EIP: 0060:[] EFLAGS: 00010206 CPU: 0 + EIP is at free_cache_attributes+0x83/0xd0 + EAX: 00000001 EBX: f589d46c ECX: 00000090 EDX: 360c2000 + ESI: 00000000 EDI: c1724a80 EBP: f58e9ec0 ESP: f58e9ea0 + DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 + CR0: 8005003b CR2: 000000ac CR3: 01731000 CR4: 000006d0 + +cache_shared_cpu_map_setup() did check sibling CPUs cacheinfo +descriptor while the respective teardown path +cache_shared_cpu_map_remove() didn't. Fix that. + +From tglx's version: to be on the safe side, move the cacheinfo +descriptor check to free_cache_attributes(), thus cleaning up +the hotplug path a little and making this even more robust. + +Reported-by: Philip Müller +Signed-off-by: Borislav Petkov +Cc: # v4.1+ +Cc: Andre Przywara +Cc: Guenter Roeck +Cc: H. Peter Anvin +Cc: Linus Torvalds +Cc: Peter Zijlstra +Cc: Sudeep Holla +Cc: Thomas Gleixner +Cc: linux-kernel@vger.kernel.org +Cc: manjaro-dev@manjaro.org +Link: http://lkml.kernel.org/r/20150727075805.GA20416@nazgul.tnic +Link: https://lkml.kernel.org/r/55B47BB8.6080202@manjaro.org +Signed-off-by: Ingo Molnar +--- + drivers/base/cacheinfo.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c +index 764280a91776..e9fd32e91668 100644 +--- a/drivers/base/cacheinfo.c ++++ b/drivers/base/cacheinfo.c +@@ -148,7 +148,11 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) + + if (sibling == cpu) /* skip itself */ + continue; ++ + sib_cpu_ci = get_cpu_cacheinfo(sibling); ++ if (!sib_cpu_ci->info_list) ++ continue; ++ + sib_leaf = sib_cpu_ci->info_list + index; + cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); + cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); +@@ -159,6 +163,9 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) + + static void free_cache_attributes(unsigned int cpu) + { ++ if (!per_cpu_cacheinfo(cpu)) ++ return; ++ + cache_shared_cpu_map_remove(cpu); + + kfree(per_cpu_cacheinfo(cpu)); +@@ -514,8 +521,7 @@ static int cacheinfo_cpu_callback(struct notifier_block *nfb, + break; + case CPU_DEAD: + cache_remove_dev(cpu); +- if (per_cpu_cacheinfo(cpu)) +- free_cache_attributes(cpu); ++ free_cache_attributes(cpu); + break; + } + return notifier_from_errno(rc); +-- +2.4.3 + diff --git a/0002-drm-i915-Rename-s-skl_compute_pipe_wm-skl_build_pipe.patch b/0002-drm-i915-Rename-s-skl_compute_pipe_wm-skl_build_pipe.patch new file mode 100644 index 000000000..f9eecb97a --- /dev/null +++ b/0002-drm-i915-Rename-s-skl_compute_pipe_wm-skl_build_pipe.patch @@ -0,0 +1,60 @@ +From 4d428f0fd6aaaa75382885d897900f619b2dad35 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 11:12:56 +0200 +Subject: [PATCH 02/17] drm/i915: Rename + s/skl_compute_pipe_wm/skl_build_pipe_wm/ + +Upstream: since drm-intel-next-2016-05-22 +commit e7649b54777ba6491204acbe1f1a34fce78637d5 + +Author: Matt Roper +AuthorDate: Thu May 12 07:05:56 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:32:27 2016 -0700 + + drm/i915: Rename s/skl_compute_pipe_wm/skl_build_pipe_wm/ + + When we added atomic watermarks, we added a new display vfunc + 'compute_pipe_wm' that is used to compute any pipe-specific watermark + information that we can at atomic check time. This was a somewhat poor + naming choice since we already had a 'skl_compute_pipe_wm' function that + doesn't quite fit this model --- the existing SKL function is something + that gets used at atomic commit time, after the DDB allocation has been + determined. Let's rename the existing SKL function to avoid confusion. + + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-3-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_pm.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 0da1d60..8f081b2 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3266,9 +3266,9 @@ static void skl_compute_transition_wm(struct intel_crtc_state *cstate, + } + } + +-static void skl_compute_pipe_wm(struct intel_crtc_state *cstate, +- struct skl_ddb_allocation *ddb, +- struct skl_pipe_wm *pipe_wm) ++static void skl_build_pipe_wm(struct intel_crtc_state *cstate, ++ struct skl_ddb_allocation *ddb, ++ struct skl_pipe_wm *pipe_wm) + { + struct drm_device *dev = cstate->base.crtc->dev; + const struct drm_i915_private *dev_priv = dev->dev_private; +@@ -3535,7 +3535,7 @@ static bool skl_update_pipe_wm(struct drm_crtc *crtc, + struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); + + skl_allocate_pipe_ddb(cstate, ddb); +- skl_compute_pipe_wm(cstate, ddb, pipe_wm); ++ skl_build_pipe_wm(cstate, ddb, pipe_wm); + + if (!memcmp(&intel_crtc->wm.active.skl, pipe_wm, sizeof(*pipe_wm))) + return false; +-- +2.7.4 + diff --git a/0002-net-dsa-mv88e6xxx-Clear-the-PDOWN-bit-on-setup.patch b/0002-net-dsa-mv88e6xxx-Clear-the-PDOWN-bit-on-setup.patch new file mode 100644 index 000000000..35b369312 --- /dev/null +++ b/0002-net-dsa-mv88e6xxx-Clear-the-PDOWN-bit-on-setup.patch @@ -0,0 +1,104 @@ +From a878e3fa9657646ff85468075823870fbbd5745f Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Wed, 30 Mar 2016 12:41:22 +0100 +Subject: [PATCH 2/2] net: dsa: mv88e6xxx: Clear the PDOWN bit on setup + +Some of the vendor-specific bootloaders set up this part +of the initialization for us, so this was never added. +However, since upstream bootloaders don't initialize the +chip specifically, they leave the fiber MII's PDOWN flag +set, which means that the CPU port doesn't connect. + +This patch checks whether this flag has been clear prior +by something else, and if not make us clear it. + +Reviewed-by: Andrew Lunn +Signed-off-by: Patrick Uiterwijk +--- + drivers/net/dsa/mv88e6xxx.c | 36 ++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/mv88e6xxx.h | 8 ++++++++ + 2 files changed, 44 insertions(+) + +diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c +index 3dcfe13..a4e3750 100644 +--- a/drivers/net/dsa/mv88e6xxx.c ++++ b/drivers/net/dsa/mv88e6xxx.c +@@ -1961,6 +1961,25 @@ restore_page_0: + return ret; + } + ++static int mv88e6xxx_power_on_serdes(struct dsa_switch *ds) ++{ ++ int ret; ++ ++ ret = _mv88e6xxx_phy_page_read(ds, REG_FIBER_SERDES, PAGE_FIBER_SERDES, ++ MII_BMCR); ++ if (ret < 0) ++ return ret; ++ ++ if (ret & BMCR_PDOWN) { ++ ret &= ~BMCR_PDOWN; ++ ret = _mv88e6xxx_phy_page_write(ds, REG_FIBER_SERDES, ++ PAGE_FIBER_SERDES, MII_BMCR, ++ ret); ++ } ++ ++ return ret; ++} ++ + static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) + { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); +@@ -2064,6 +2083,23 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) + goto abort; + } + ++ /* If this port is connected to a SerDes, make sure the SerDes is not ++ * powered down. ++ */ ++ if (mv88e6xxx_6352_family(ds)) { ++ ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS); ++ if (ret < 0) ++ goto abort; ++ ret &= PORT_STATUS_CMODE_MASK; ++ if ((ret == PORT_STATUS_CMODE_100BASE_X) || ++ (ret == PORT_STATUS_CMODE_1000BASE_X) || ++ (ret == PORT_STATUS_CMODE_SGMII)) { ++ ret = mv88e6xxx_power_on_serdes(ds); ++ if (ret < 0) ++ goto abort; ++ } ++ } ++ + /* Port Control 2: don't force a good FCS, set the maximum frame size to + * 10240 bytes, enable secure 802.1q tags, don't discard tagged or + * untagged frames on this port, do a destination address lookup on all +diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h +index ca08f91..adc7f0d 100644 +--- a/drivers/net/dsa/mv88e6xxx.h ++++ b/drivers/net/dsa/mv88e6xxx.h +@@ -28,6 +28,10 @@ + #define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY) + #define SMI_DATA 0x01 + ++/* Fiber/SERDES Registers are located at SMI address F, page 1 */ ++#define REG_FIBER_SERDES 0x0f ++#define PAGE_FIBER_SERDES 0x01 ++ + #define REG_PORT(p) (0x10 + (p)) + #define PORT_STATUS 0x00 + #define PORT_STATUS_PAUSE_EN BIT(15) +@@ -45,6 +49,10 @@ + #define PORT_STATUS_MGMII BIT(6) /* 6185 */ + #define PORT_STATUS_TX_PAUSED BIT(5) + #define PORT_STATUS_FLOW_CTRL BIT(4) ++#define PORT_STATUS_CMODE_MASK 0x0f ++#define PORT_STATUS_CMODE_100BASE_X 0x8 ++#define PORT_STATUS_CMODE_1000BASE_X 0x9 ++#define PORT_STATUS_CMODE_SGMII 0xa + #define PORT_PCS_CTRL 0x01 + #define PORT_PCS_CTRL_RGMII_DELAY_RXCLK BIT(15) + #define PORT_PCS_CTRL_RGMII_DELAY_TXCLK BIT(14) +-- +2.7.3 + diff --git a/0003-drm-i915-gen9-Cache-plane-data-rates-in-CRTC-state.patch b/0003-drm-i915-gen9-Cache-plane-data-rates-in-CRTC-state.patch new file mode 100644 index 000000000..9a9366c1c --- /dev/null +++ b/0003-drm-i915-gen9-Cache-plane-data-rates-in-CRTC-state.patch @@ -0,0 +1,214 @@ +From 0206aec944641c69815562407b73b6f9df22f041 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 11:13:09 +0200 +Subject: [PATCH 03/17] drm/i915/gen9: Cache plane data rates in CRTC state + +Upstream: since drm-intel-next-2016-05-22 +commit a1de91e5f3039dfc32ac2b77ffb280a68646cbc7 + +Author: Matt Roper +AuthorDate: Thu May 12 07:05:57 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:32:35 2016 -0700 + + drm/i915/gen9: Cache plane data rates in CRTC state + + This will be important when we start calculating CRTC data rates for + in-flight CRTC states since it will allow us to calculate the total data + rate without needing to grab the plane state for any planes that aren't + updated by the transaction. + + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-4-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_drv.h | 4 ++ + drivers/gpu/drm/i915/intel_pm.c | 92 ++++++++++++++++++++++++++-------------- + 2 files changed, 63 insertions(+), 33 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index 7d19baf..7c00ab6 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -385,6 +385,10 @@ struct intel_crtc_wm_state { + struct { + /* gen9+ only needs 1-step wm programming */ + struct skl_pipe_wm optimal; ++ ++ /* cached plane data rate */ ++ unsigned plane_data_rate[I915_MAX_PLANES]; ++ unsigned plane_y_data_rate[I915_MAX_PLANES]; + } skl; + }; + +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 8f081b2..854f0a4 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -2879,6 +2879,14 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate, + struct intel_plane_state *intel_pstate = to_intel_plane_state(pstate); + struct drm_framebuffer *fb = pstate->fb; + uint32_t width = 0, height = 0; ++ unsigned format = fb ? fb->pixel_format : DRM_FORMAT_XRGB8888; ++ ++ if (!intel_pstate->visible) ++ return 0; ++ if (pstate->plane->type == DRM_PLANE_TYPE_CURSOR) ++ return 0; ++ if (y && format != DRM_FORMAT_NV12) ++ return 0; + + width = drm_rect_width(&intel_pstate->src) >> 16; + height = drm_rect_height(&intel_pstate->src) >> 16; +@@ -2887,17 +2895,17 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate, + swap(width, height); + + /* for planar format */ +- if (fb->pixel_format == DRM_FORMAT_NV12) { ++ if (format == DRM_FORMAT_NV12) { + if (y) /* y-plane data rate */ + return width * height * +- drm_format_plane_cpp(fb->pixel_format, 0); ++ drm_format_plane_cpp(format, 0); + else /* uv-plane data rate */ + return (width / 2) * (height / 2) * +- drm_format_plane_cpp(fb->pixel_format, 1); ++ drm_format_plane_cpp(format, 1); + } + + /* for packed formats */ +- return width * height * drm_format_plane_cpp(fb->pixel_format, 0); ++ return width * height * drm_format_plane_cpp(format, 0); + } + + /* +@@ -2906,32 +2914,34 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate, + * 3 * 4096 * 8192 * 4 < 2^32 + */ + static unsigned int +-skl_get_total_relative_data_rate(const struct intel_crtc_state *cstate) ++skl_get_total_relative_data_rate(struct intel_crtc_state *cstate) + { + struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc); + struct drm_device *dev = intel_crtc->base.dev; + const struct intel_plane *intel_plane; +- unsigned int total_data_rate = 0; ++ unsigned int rate, total_data_rate = 0; + ++ /* Calculate and cache data rate for each plane */ + for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { + const struct drm_plane_state *pstate = intel_plane->base.state; ++ int id = skl_wm_plane_id(intel_plane); + +- if (pstate->fb == NULL) +- continue; ++ /* packed/uv */ ++ rate = skl_plane_relative_data_rate(cstate, pstate, 0); ++ cstate->wm.skl.plane_data_rate[id] = rate; + +- if (intel_plane->base.type == DRM_PLANE_TYPE_CURSOR) +- continue; ++ /* y-plane */ ++ rate = skl_plane_relative_data_rate(cstate, pstate, 1); ++ cstate->wm.skl.plane_y_data_rate[id] = rate; ++ } + +- /* packed/uv */ +- total_data_rate += skl_plane_relative_data_rate(cstate, +- pstate, +- 0); ++ /* Calculate CRTC's total data rate from cached values */ ++ for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { ++ int id = skl_wm_plane_id(intel_plane); + +- if (pstate->fb->pixel_format == DRM_FORMAT_NV12) +- /* y-plane */ +- total_data_rate += skl_plane_relative_data_rate(cstate, +- pstate, +- 1); ++ /* packed/uv */ ++ total_data_rate += cstate->wm.skl.plane_data_rate[id]; ++ total_data_rate += cstate->wm.skl.plane_y_data_rate[id]; + } + + return total_data_rate; +@@ -2995,6 +3005,8 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + * FIXME: we may not allocate every single block here. + */ + total_data_rate = skl_get_total_relative_data_rate(cstate); ++ if (total_data_rate == 0) ++ return; + + start = alloc->start; + for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { +@@ -3009,7 +3021,7 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + if (plane->type == DRM_PLANE_TYPE_CURSOR) + continue; + +- data_rate = skl_plane_relative_data_rate(cstate, pstate, 0); ++ data_rate = cstate->wm.skl.plane_data_rate[id]; + + /* + * allocation for (packed formats) or (uv-plane part of planar format): +@@ -3028,20 +3040,16 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + /* + * allocation for y_plane part of planar format: + */ +- if (pstate->fb->pixel_format == DRM_FORMAT_NV12) { +- y_data_rate = skl_plane_relative_data_rate(cstate, +- pstate, +- 1); +- y_plane_blocks = y_minimum[id]; +- y_plane_blocks += div_u64((uint64_t)alloc_size * y_data_rate, +- total_data_rate); +- +- ddb->y_plane[pipe][id].start = start; +- ddb->y_plane[pipe][id].end = start + y_plane_blocks; +- +- start += y_plane_blocks; +- } ++ y_data_rate = cstate->wm.skl.plane_y_data_rate[id]; ++ ++ y_plane_blocks = y_minimum[id]; ++ y_plane_blocks += div_u64((uint64_t)alloc_size * y_data_rate, ++ total_data_rate); + ++ ddb->y_plane[pipe][id].start = start; ++ ddb->y_plane[pipe][id].end = start + y_plane_blocks; ++ ++ start += y_plane_blocks; + } + + } +@@ -3820,10 +3828,28 @@ void skl_wm_get_hw_state(struct drm_device *dev) + struct drm_i915_private *dev_priv = dev->dev_private; + struct skl_ddb_allocation *ddb = &dev_priv->wm.skl_hw.ddb; + struct drm_crtc *crtc; ++ struct intel_crtc *intel_crtc; + + skl_ddb_get_hw_state(dev_priv, ddb); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + skl_pipe_wm_get_hw_state(crtc); ++ ++ /* Calculate plane data rates */ ++ for_each_intel_crtc(dev, intel_crtc) { ++ struct intel_crtc_state *cstate = intel_crtc->config; ++ struct intel_plane *intel_plane; ++ ++ for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { ++ const struct drm_plane_state *pstate = ++ intel_plane->base.state; ++ int id = skl_wm_plane_id(intel_plane); ++ ++ cstate->wm.skl.plane_data_rate[id] = ++ skl_plane_relative_data_rate(cstate, pstate, 0); ++ cstate->wm.skl.plane_y_data_rate[id] = ++ skl_plane_relative_data_rate(cstate, pstate, 1); ++ } ++ } + } + + static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc) +-- +2.7.4 + diff --git a/0004-drm-i915-gen9-Allow-calculation-of-data-rate-for-in-.patch b/0004-drm-i915-gen9-Allow-calculation-of-data-rate-for-in-.patch new file mode 100644 index 000000000..3a2b0aa1c --- /dev/null +++ b/0004-drm-i915-gen9-Allow-calculation-of-data-rate-for-in-.patch @@ -0,0 +1,141 @@ +From a75a3c793e2cd3e7648597f2c77d87453f520f31 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 11:13:23 +0200 +Subject: [PATCH 04/17] drm/i915/gen9: Allow calculation of data rate for + in-flight state (v2) + +Upstream: since drm-intel-next-2016-05-22 +commit 9c74d82621c553f0da1f41bd5d90f5eab5659fdb + +Author: Matt Roper +AuthorDate: Thu May 12 07:05:58 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:32:49 2016 -0700 + + drm/i915/gen9: Allow calculation of data rate for in-flight state (v2) + + Our skl_get_total_relative_data_rate() function gets passed a crtc state + object to calculate the data rate for, but it currently always looks + up the committed plane states that correspond to that CRTC. Let's + check whether the CRTC state is an in-flight state (meaning + cstate->state is non-NULL) and if so, use the corresponding in-flight + plane states. + + We'll soon be using this function exclusively for in-flight states; at + that time we'll be able to simplify the function a bit, but for now we + allow it to be used in either mode. + + v2: + - Rebase on top of changes to cache plane data rates. + + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-5-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_pm.c | 74 +++++++++++++++++++++++++++++++++-------- + 1 file changed, 60 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 854f0a4..b863bfc 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -2914,25 +2914,69 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate, + * 3 * 4096 * 8192 * 4 < 2^32 + */ + static unsigned int +-skl_get_total_relative_data_rate(struct intel_crtc_state *cstate) ++skl_get_total_relative_data_rate(struct intel_crtc_state *intel_cstate) + { +- struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc); +- struct drm_device *dev = intel_crtc->base.dev; ++ struct drm_crtc_state *cstate = &intel_cstate->base; ++ struct drm_atomic_state *state = cstate->state; ++ struct drm_crtc *crtc = cstate->crtc; ++ struct drm_device *dev = crtc->dev; ++ struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + const struct intel_plane *intel_plane; + unsigned int rate, total_data_rate = 0; ++ int id; + + /* Calculate and cache data rate for each plane */ +- for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { +- const struct drm_plane_state *pstate = intel_plane->base.state; +- int id = skl_wm_plane_id(intel_plane); ++ /* ++ * FIXME: At the moment this function can be called on either an ++ * in-flight or a committed state object. If it's in-flight then we ++ * only want to re-calculate the plane data rate for planes that are ++ * part of the transaction (i.e., we don't want to grab any additional ++ * plane states if we don't have to). If we're operating on committed ++ * state, we'll just go ahead and recalculate the plane data rate for ++ * all planes. ++ * ++ * Once we finish moving our DDB allocation to the atomic check phase, ++ * we'll only be calling this function on in-flight state objects, so ++ * the 'else' branch here will go away. ++ */ ++ if (state) { ++ struct drm_plane *plane; ++ struct drm_plane_state *pstate; ++ int i; ++ ++ for_each_plane_in_state(state, plane, pstate, i) { ++ intel_plane = to_intel_plane(plane); ++ id = skl_wm_plane_id(intel_plane); ++ ++ if (intel_plane->pipe != intel_crtc->pipe) ++ continue; ++ ++ /* packed/uv */ ++ rate = skl_plane_relative_data_rate(intel_cstate, ++ pstate, 0); ++ intel_cstate->wm.skl.plane_data_rate[id] = rate; ++ ++ /* y-plane */ ++ rate = skl_plane_relative_data_rate(intel_cstate, ++ pstate, 1); ++ intel_cstate->wm.skl.plane_y_data_rate[id] = rate; ++ } ++ } else { ++ for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { ++ const struct drm_plane_state *pstate = ++ intel_plane->base.state; ++ int id = skl_wm_plane_id(intel_plane); + +- /* packed/uv */ +- rate = skl_plane_relative_data_rate(cstate, pstate, 0); +- cstate->wm.skl.plane_data_rate[id] = rate; ++ /* packed/uv */ ++ rate = skl_plane_relative_data_rate(intel_cstate, ++ pstate, 0); ++ intel_cstate->wm.skl.plane_data_rate[id] = rate; + +- /* y-plane */ +- rate = skl_plane_relative_data_rate(cstate, pstate, 1); +- cstate->wm.skl.plane_y_data_rate[id] = rate; ++ /* y-plane */ ++ rate = skl_plane_relative_data_rate(intel_cstate, ++ pstate, 1); ++ intel_cstate->wm.skl.plane_y_data_rate[id] = rate; ++ } + } + + /* Calculate CRTC's total data rate from cached values */ +@@ -2940,10 +2984,12 @@ skl_get_total_relative_data_rate(struct intel_crtc_state *cstate) + int id = skl_wm_plane_id(intel_plane); + + /* packed/uv */ +- total_data_rate += cstate->wm.skl.plane_data_rate[id]; +- total_data_rate += cstate->wm.skl.plane_y_data_rate[id]; ++ total_data_rate += intel_cstate->wm.skl.plane_data_rate[id]; ++ total_data_rate += intel_cstate->wm.skl.plane_y_data_rate[id]; + } + ++ WARN_ON(cstate->plane_mask && total_data_rate == 0); ++ + return total_data_rate; + } + +-- +2.7.4 + diff --git a/0005-drm-i915-gen9-Store-plane-minimum-blocks-in-CRTC-wm-.patch b/0005-drm-i915-gen9-Store-plane-minimum-blocks-in-CRTC-wm-.patch new file mode 100644 index 000000000..7b1fcb144 --- /dev/null +++ b/0005-drm-i915-gen9-Store-plane-minimum-blocks-in-CRTC-wm-.patch @@ -0,0 +1,67 @@ +From cd21ce996b94fd149b3487398e5250e9f0cc8811 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 12:39:24 +0200 +Subject: [PATCH 05/17] drm/i915/gen9: Store plane minimum blocks in CRTC wm + state (v2) + +Upstream: since drm-intel-next-2016-05-22 +commit 86a2100a8b96594902bb59b90614377df4f64ce0 + +Author: Matt Roper +AuthorDate: Thu May 12 07:05:59 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:32:56 2016 -0700 + + drm/i915/gen9: Store plane minimum blocks in CRTC wm state (v2) + + This will eventually allow us to re-use old values without + re-calculating them for unchanged planes (which also helps us avoid + re-grabbing extra plane states). + + v2: + - Drop unnecessary memset's; they were meant for a later patch (which + got reworked anyway to not need them, but were mis-rebased into this + one. (Maarten) + + Cc: Maarten Lankhorst + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-6-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_drv.h | 4 ++++ + drivers/gpu/drm/i915/intel_pm.c | 4 ++-- + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index 7c00ab6..d246308 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -389,6 +389,10 @@ struct intel_crtc_wm_state { + /* cached plane data rate */ + unsigned plane_data_rate[I915_MAX_PLANES]; + unsigned plane_y_data_rate[I915_MAX_PLANES]; ++ ++ /* minimum block allocation */ ++ uint16_t minimum_blocks[I915_MAX_PLANES]; ++ uint16_t minimum_y_blocks[I915_MAX_PLANES]; + } skl; + }; + +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index b863bfc..00db6e9 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3006,8 +3006,8 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + enum pipe pipe = intel_crtc->pipe; + struct skl_ddb_entry *alloc = &ddb->pipe[pipe]; + uint16_t alloc_size, start, cursor_blocks; +- uint16_t minimum[I915_MAX_PLANES]; +- uint16_t y_minimum[I915_MAX_PLANES]; ++ uint16_t *minimum = cstate->wm.skl.minimum_blocks; ++ uint16_t *y_minimum = cstate->wm.skl.minimum_y_blocks; + unsigned int total_data_rate; + + skl_ddb_get_pipe_allocation_limits(dev, cstate, config, alloc); +-- +2.7.4 + diff --git a/0006-drm-i915-Track-whether-an-atomic-transaction-changes.patch b/0006-drm-i915-Track-whether-an-atomic-transaction-changes.patch new file mode 100644 index 000000000..7320abf6d --- /dev/null +++ b/0006-drm-i915-Track-whether-an-atomic-transaction-changes.patch @@ -0,0 +1,68 @@ +From 00edb23bcefa3ad6931f2a2855fe0801a55523f7 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 12:39:40 +0200 +Subject: [PATCH 06/17] drm/i915: Track whether an atomic transaction changes + the active CRTC's + +Upstream: since drm-intel-next-2016-05-22 +commit 8b4a7d0597cd9910d7127ffae6ae91d21853a8a2 + +Author: Matt Roper +AuthorDate: Thu May 12 07:06:00 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:33:10 2016 -0700 + + drm/i915: Track whether an atomic transaction changes the active CRTC's + + For the purposes of DDB re-allocation we need to know whether a + transaction changes the list of CRTC's that are active. While + state->modeset could be used for this purpose, that would be slightly + too aggressive since it would lead us to re-allocate the DDB when a + CRTC's mode changes, but not its final active state. + + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-7-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_display.c | 3 +++ + drivers/gpu/drm/i915/intel_drv.h | 10 ++++++++++ + 2 files changed, 13 insertions(+) + +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index 7d855ba..f53df81 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -13183,6 +13183,9 @@ static int intel_modeset_checks(struct drm_atomic_state *state) + intel_state->active_crtcs |= 1 << i; + else + intel_state->active_crtcs &= ~(1 << i); ++ ++ if (crtc_state->active != crtc->state->active) ++ intel_state->active_pipe_changes |= drm_crtc_mask(crtc); + } + + /* +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index d246308..672ca56 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -256,6 +256,16 @@ struct intel_atomic_state { + + bool dpll_set, modeset; + ++ /* ++ * Does this transaction change the pipes that are active? This mask ++ * tracks which CRTC's have changed their active state at the end of ++ * the transaction (not counting the temporary disable during modesets). ++ * This mask should only be non-zero when intel_state->modeset is true, ++ * but the converse is not necessarily true; simply changing a mode may ++ * not flip the final active status of any CRTC's ++ */ ++ unsigned int active_pipe_changes; ++ + unsigned int active_crtcs; + unsigned int min_pixclk[I915_MAX_PIPES]; + +-- +2.7.4 + diff --git a/0007-drm-i915-gen9-Allow-skl_allocate_pipe_ddb-to-operate.patch b/0007-drm-i915-gen9-Allow-skl_allocate_pipe_ddb-to-operate.patch new file mode 100644 index 000000000..deb90fb01 --- /dev/null +++ b/0007-drm-i915-gen9-Allow-skl_allocate_pipe_ddb-to-operate.patch @@ -0,0 +1,352 @@ +From 99dd9c3733696d4a2536b21988c9b1b8f5195c5b Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 12:40:00 +0200 +Subject: [PATCH 07/17] drm/i915/gen9: Allow skl_allocate_pipe_ddb() to operate + on in-flight state (v3) + +Upstream: since drm-intel-next-2016-05-22 +commit c107acfeb03187873657ccc8af4fc5c704b3626b + +Author: Matt Roper +AuthorDate: Thu May 12 07:06:01 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:33:16 2016 -0700 + + drm/i915/gen9: Allow skl_allocate_pipe_ddb() to operate on in-flight state (v3) + + We eventually want to calculate watermark values at atomic 'check' time + instead of atomic 'commit' time so that any requested configurations + that result in impossible watermark requirements are properly rejected. + The first step along this path is to allocate the DDB at atomic 'check' + time. As we perform this transition, allow the main allocation function + to operate successfully on either an in-flight state or an + already-commited state. Once we complete the transition in a future + patch, we'll come back and remove the unnecessary logic for the + already-committed case. + + v2: Rebase/refactor; we should no longer need to grab extra plane states + while allocating the DDB since we can pull cached data rates and + minimum block counts from the CRTC state for any planes that aren't + being modified by this transaction. + + v3: + - Simplify memsets to clear DDB plane entries. (Maarten) + - Drop a redundant memset of plane[pipe][PLANE_CURSOR] that was added + by an earlier Coccinelle patch. (Maarten) + - Assign *num_active at the top of skl_ddb_get_pipe_allocation_limits() + so that no code paths return without setting it. (kbuild robot) + + Cc: Maarten Lankhorst + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-8-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/i915_drv.h | 6 ++ + drivers/gpu/drm/i915/intel_pm.c | 179 +++++++++++++++++++++++++++++----------- + 2 files changed, 139 insertions(+), 46 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index daba7eb..804af6f 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -281,6 +281,12 @@ struct i915_hotplug { + &dev->mode_config.plane_list, \ + base.head) + ++#define for_each_intel_plane_mask(dev, intel_plane, plane_mask) \ ++ list_for_each_entry(intel_plane, &dev->mode_config.plane_list, \ ++ base.head) \ ++ for_each_if ((plane_mask) & \ ++ (1 << drm_plane_index(&intel_plane->base))) ++ + #define for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) \ + list_for_each_entry(intel_plane, \ + &(dev)->mode_config.plane_list, \ +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 00db6e9..ee82b1f 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -2788,13 +2788,25 @@ skl_wm_plane_id(const struct intel_plane *plane) + static void + skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, + const struct intel_crtc_state *cstate, +- const struct intel_wm_config *config, +- struct skl_ddb_entry *alloc /* out */) ++ struct intel_wm_config *config, ++ struct skl_ddb_entry *alloc, /* out */ ++ int *num_active /* out */) + { ++ struct drm_atomic_state *state = cstate->base.state; ++ struct intel_atomic_state *intel_state = to_intel_atomic_state(state); ++ struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_crtc *for_crtc = cstate->base.crtc; + struct drm_crtc *crtc; + unsigned int pipe_size, ddb_size; + int nth_active_pipe; ++ int pipe = to_intel_crtc(for_crtc)->pipe; ++ ++ if (intel_state && intel_state->active_pipe_changes) ++ *num_active = hweight32(intel_state->active_crtcs); ++ else if (intel_state) ++ *num_active = hweight32(dev_priv->active_crtcs); ++ else ++ *num_active = config->num_pipes_active; + + if (!cstate->base.active) { + alloc->start = 0; +@@ -2809,25 +2821,56 @@ skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, + + ddb_size -= 4; /* 4 blocks for bypass path allocation */ + +- nth_active_pipe = 0; +- for_each_crtc(dev, crtc) { +- if (!to_intel_crtc(crtc)->active) +- continue; ++ /* ++ * FIXME: At the moment we may be called on either in-flight or fully ++ * committed cstate's. Once we fully move DDB allocation in the check ++ * phase, we'll only be called on in-flight states and the 'else' ++ * branch here will go away. ++ * ++ * The 'else' branch is slightly racy here, but it was racy to begin ++ * with; since it's going away soon, no effort is made to address that. ++ */ ++ if (state) { ++ /* ++ * If the state doesn't change the active CRTC's, then there's ++ * no need to recalculate; the existing pipe allocation limits ++ * should remain unchanged. Note that we're safe from racing ++ * commits since any racing commit that changes the active CRTC ++ * list would need to grab _all_ crtc locks, including the one ++ * we currently hold. ++ */ ++ if (!intel_state->active_pipe_changes) { ++ *alloc = dev_priv->wm.skl_hw.ddb.pipe[pipe]; ++ return; ++ } + +- if (crtc == for_crtc) +- break; ++ nth_active_pipe = hweight32(intel_state->active_crtcs & ++ (drm_crtc_mask(for_crtc) - 1)); ++ pipe_size = ddb_size / hweight32(intel_state->active_crtcs); ++ alloc->start = nth_active_pipe * ddb_size / *num_active; ++ alloc->end = alloc->start + pipe_size; ++ } else { ++ nth_active_pipe = 0; ++ for_each_crtc(dev, crtc) { ++ if (!to_intel_crtc(crtc)->active) ++ continue; + +- nth_active_pipe++; +- } ++ if (crtc == for_crtc) ++ break; ++ ++ nth_active_pipe++; ++ } + +- pipe_size = ddb_size / config->num_pipes_active; +- alloc->start = nth_active_pipe * ddb_size / config->num_pipes_active; +- alloc->end = alloc->start + pipe_size; ++ pipe_size = ddb_size / config->num_pipes_active; ++ alloc->start = nth_active_pipe * ddb_size / ++ config->num_pipes_active; ++ alloc->end = alloc->start + pipe_size; ++ } + } + +-static unsigned int skl_cursor_allocation(const struct intel_wm_config *config) ++static unsigned int skl_cursor_allocation(int num_active) + { +- if (config->num_pipes_active == 1) ++ if (num_active == 1) + return 32; + + return 8; +@@ -2993,33 +3036,44 @@ skl_get_total_relative_data_rate(struct intel_crtc_state *intel_cstate) + return total_data_rate; + } + +-static void ++static int + skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + struct skl_ddb_allocation *ddb /* out */) + { ++ struct drm_atomic_state *state = cstate->base.state; + struct drm_crtc *crtc = cstate->base.crtc; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_wm_config *config = &dev_priv->wm.config; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_plane *intel_plane; ++ struct drm_plane *plane; ++ struct drm_plane_state *pstate; + enum pipe pipe = intel_crtc->pipe; + struct skl_ddb_entry *alloc = &ddb->pipe[pipe]; + uint16_t alloc_size, start, cursor_blocks; + uint16_t *minimum = cstate->wm.skl.minimum_blocks; + uint16_t *y_minimum = cstate->wm.skl.minimum_y_blocks; + unsigned int total_data_rate; ++ int num_active; ++ int id, i; ++ ++ if (!cstate->base.active) { ++ ddb->pipe[pipe].start = ddb->pipe[pipe].end = 0; ++ memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe])); ++ memset(ddb->y_plane[pipe], 0, sizeof(ddb->y_plane[pipe])); ++ return 0; ++ } + +- skl_ddb_get_pipe_allocation_limits(dev, cstate, config, alloc); ++ skl_ddb_get_pipe_allocation_limits(dev, cstate, config, alloc, ++ &num_active); + alloc_size = skl_ddb_entry_size(alloc); + if (alloc_size == 0) { + memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe])); +- memset(&ddb->plane[pipe][PLANE_CURSOR], 0, +- sizeof(ddb->plane[pipe][PLANE_CURSOR])); +- return; ++ return 0; + } + +- cursor_blocks = skl_cursor_allocation(config); ++ cursor_blocks = skl_cursor_allocation(num_active); + ddb->plane[pipe][PLANE_CURSOR].start = alloc->end - cursor_blocks; + ddb->plane[pipe][PLANE_CURSOR].end = alloc->end; + +@@ -3027,21 +3081,55 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + alloc->end -= cursor_blocks; + + /* 1. Allocate the mininum required blocks for each active plane */ +- for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { +- struct drm_plane *plane = &intel_plane->base; +- struct drm_framebuffer *fb = plane->state->fb; +- int id = skl_wm_plane_id(intel_plane); ++ /* ++ * TODO: Remove support for already-committed state once we ++ * only allocate DDB on in-flight states. ++ */ ++ if (state) { ++ for_each_plane_in_state(state, plane, pstate, i) { ++ intel_plane = to_intel_plane(plane); ++ id = skl_wm_plane_id(intel_plane); + +- if (!to_intel_plane_state(plane->state)->visible) +- continue; ++ if (intel_plane->pipe != pipe) ++ continue; + +- if (plane->type == DRM_PLANE_TYPE_CURSOR) +- continue; ++ if (!to_intel_plane_state(pstate)->visible) { ++ minimum[id] = 0; ++ y_minimum[id] = 0; ++ continue; ++ } ++ if (plane->type == DRM_PLANE_TYPE_CURSOR) { ++ minimum[id] = 0; ++ y_minimum[id] = 0; ++ continue; ++ } + +- minimum[id] = 8; +- alloc_size -= minimum[id]; +- y_minimum[id] = (fb->pixel_format == DRM_FORMAT_NV12) ? 8 : 0; +- alloc_size -= y_minimum[id]; ++ minimum[id] = 8; ++ if (pstate->fb->pixel_format == DRM_FORMAT_NV12) ++ y_minimum[id] = 8; ++ else ++ y_minimum[id] = 0; ++ } ++ } else { ++ for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { ++ struct drm_plane *plane = &intel_plane->base; ++ struct drm_framebuffer *fb = plane->state->fb; ++ int id = skl_wm_plane_id(intel_plane); ++ ++ if (!to_intel_plane_state(plane->state)->visible) ++ continue; ++ ++ if (plane->type == DRM_PLANE_TYPE_CURSOR) ++ continue; ++ ++ minimum[id] = 8; ++ y_minimum[id] = (fb->pixel_format == DRM_FORMAT_NV12) ? 8 : 0; ++ } ++ } ++ ++ for (i = 0; i < PLANE_CURSOR; i++) { ++ alloc_size -= minimum[i]; ++ alloc_size -= y_minimum[i]; + } + + /* +@@ -3052,21 +3140,14 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + */ + total_data_rate = skl_get_total_relative_data_rate(cstate); + if (total_data_rate == 0) +- return; ++ return 0; + + start = alloc->start; + for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { +- struct drm_plane *plane = &intel_plane->base; +- struct drm_plane_state *pstate = intel_plane->base.state; + unsigned int data_rate, y_data_rate; + uint16_t plane_blocks, y_plane_blocks = 0; + int id = skl_wm_plane_id(intel_plane); + +- if (!to_intel_plane_state(pstate)->visible) +- continue; +- if (plane->type == DRM_PLANE_TYPE_CURSOR) +- continue; +- + data_rate = cstate->wm.skl.plane_data_rate[id]; + + /* +@@ -3078,8 +3159,11 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + plane_blocks += div_u64((uint64_t)alloc_size * data_rate, + total_data_rate); + +- ddb->plane[pipe][id].start = start; +- ddb->plane[pipe][id].end = start + plane_blocks; ++ /* Leave disabled planes at (0,0) */ ++ if (data_rate) { ++ ddb->plane[pipe][id].start = start; ++ ddb->plane[pipe][id].end = start + plane_blocks; ++ } + + start += plane_blocks; + +@@ -3092,12 +3176,15 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + y_plane_blocks += div_u64((uint64_t)alloc_size * y_data_rate, + total_data_rate); + +- ddb->y_plane[pipe][id].start = start; +- ddb->y_plane[pipe][id].end = start + y_plane_blocks; ++ if (y_data_rate) { ++ ddb->y_plane[pipe][id].start = start; ++ ddb->y_plane[pipe][id].end = start + y_plane_blocks; ++ } + + start += y_plane_blocks; + } + ++ return 0; + } + + static uint32_t skl_pipe_pixel_rate(const struct intel_crtc_state *config) +@@ -3588,7 +3675,7 @@ static bool skl_update_pipe_wm(struct drm_crtc *crtc, + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); + +- skl_allocate_pipe_ddb(cstate, ddb); ++ WARN_ON(skl_allocate_pipe_ddb(cstate, ddb) != 0); + skl_build_pipe_wm(cstate, ddb, pipe_wm); + + if (!memcmp(&intel_crtc->wm.active.skl, pipe_wm, sizeof(*pipe_wm))) +-- +2.7.4 + diff --git a/0008-drm-i915-Add-distrust_bios_wm-flag-to-dev_priv-v2.patch b/0008-drm-i915-Add-distrust_bios_wm-flag-to-dev_priv-v2.patch new file mode 100644 index 000000000..9131a60da --- /dev/null +++ b/0008-drm-i915-Add-distrust_bios_wm-flag-to-dev_priv-v2.patch @@ -0,0 +1,84 @@ +From 7207eecfcb3095442bce30227b551003edc7b908 Mon Sep 17 00:00:00 2001 +From: Matt Roper +Date: Thu, 12 May 2016 07:06:02 -0700 +Subject: [PATCH 08/17] drm/i915: Add distrust_bios_wm flag to dev_priv (v2) + +SKL-style platforms can't fully trust the watermark/DDB settings +programmed by the BIOS and need to do extra sanitization on their first +atomic update. Add a flag to dev_priv that is set during hardware +readout and cleared at the end of the first commit. + +Note that for the somewhat common case where everything is turned off +when the driver starts up, we don't need to bother with a recompute...we +know exactly what the DDB should be (all zero's) so just setup the DDB +directly in that case. + +v2: + - Move clearing of distrust_bios_wm up below the swap_state call since + it's a more natural / self-explanatory location. (Maarten) + - Use dev_priv->active_crtcs to test whether any CRTC's are turned on + during HW WM readout rather than trying to count the active CRTC's + again ourselves. (Maarten) + +Cc: Maarten Lankhorst +Signed-off-by: Matt Roper +Reviewed-by: Maarten Lankhorst +Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-9-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/i915_drv.h | 7 +++++++ + drivers/gpu/drm/i915/intel_display.c | 1 + + drivers/gpu/drm/i915/intel_pm.c | 8 ++++++++ + 3 files changed, 16 insertions(+) + +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index 611c128..e21960d 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -1981,6 +1981,13 @@ struct drm_i915_private { + * cstate->wm.need_postvbl_update. + */ + struct mutex wm_mutex; ++ ++ /* ++ * Set during HW readout of watermarks/DDB. Some platforms ++ * need to know when we're still using BIOS-provided values ++ * (which we don't fully trust). ++ */ ++ bool distrust_bios_wm; + } wm; + + struct i915_runtime_pm pm; +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index f26d1c5..a9d2e30 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -13621,6 +13621,7 @@ static int intel_atomic_commit(struct drm_device *dev, + + drm_atomic_helper_swap_state(dev, state); + dev_priv->wm.config = intel_state->wm_config; ++ dev_priv->wm.distrust_bios_wm = false; + intel_shared_dpll_commit(state); + + if (intel_state->modeset) { +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index f009d43..a49faa7 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -4026,6 +4026,14 @@ void skl_wm_get_hw_state(struct drm_device *dev) + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + skl_pipe_wm_get_hw_state(crtc); + ++ if (dev_priv->active_crtcs) { ++ /* Fully recompute DDB on first atomic commit */ ++ dev_priv->wm.distrust_bios_wm = true; ++ } else { ++ /* Easy/common case; just sanitize DDB now if everything off */ ++ memset(ddb, 0, sizeof(*ddb)); ++ } ++ + /* Calculate plane data rates */ + for_each_intel_crtc(dev, intel_crtc) { + struct intel_crtc_state *cstate = intel_crtc->config; +-- +2.7.4 + diff --git a/0009-drm-i915-gen9-Compute-DDB-allocation-at-atomic-check.patch b/0009-drm-i915-gen9-Compute-DDB-allocation-at-atomic-check.patch new file mode 100644 index 000000000..80cdacf9a --- /dev/null +++ b/0009-drm-i915-gen9-Compute-DDB-allocation-at-atomic-check.patch @@ -0,0 +1,244 @@ +From fbf53d8f1b7d1bcea1411f1f2cd0df6a6cc95332 Mon Sep 17 00:00:00 2001 +From: Matt Roper +Date: Thu, 12 May 2016 07:06:03 -0700 +Subject: [PATCH 09/17] drm/i915/gen9: Compute DDB allocation at atomic check + time (v4) + +Calculate the DDB blocks needed to satisfy the current atomic +transaction at atomic check time. This is a prerequisite to calculating +SKL watermarks during the 'check' phase and rejecting any configurations +that we can't find valid watermarks for. + +Due to the nature of DDB allocation, it's possible for the addition of a +new CRTC to make the watermark configuration already in use on another, +unchanged CRTC become invalid. A change in which CRTC's are active +triggers a recompute of the entire DDB, which unfortunately means we +need to disallow any other atomic commits from racing with such an +update. If the active CRTC's change, we need to grab the lock on all +CRTC's and run all CRTC's through their 'check' handler to recompute and +re-check their per-CRTC DDB allocations. + +Note that with this patch we only compute the DDB allocation but we +don't actually use the computed values during watermark programming yet. +For ease of review/testing/bisecting, we still recompute the DDB at +watermark programming time and just WARN() if it doesn't match the +precomputed values. A future patch will switch over to using the +precomputed values once we're sure they're being properly computed. + +Another clarifying note: DDB allocation itself shouldn't ever fail with +the algorithm we use today (i.e., we have enough DDB blocks on BXT to +support the minimum needs of the worst-case scenario of every pipe/plane +enabled at full size). However the watermarks calculations based on the +DDB may fail and we'll be moving those to the atomic check as well in +future patches. + +v2: + - Skip DDB calculations in the rare case where our transaction doesn't + actually touch any CRTC's at all. Assuming at least one CRTC state + is present in our transaction, then it means we can't race with any + transactions that would update dev_priv->active_crtcs (which requires + _all_ CRTC locks). + +v3: + - Also calculate DDB during initial hw readout, to prevent using + incorrect bios values. (Maarten) + +v4: + - Use new distrust_bios_wm flag instead of skip_initial_wm (which was + never actually set). + - Set intel_state->active_pipe_changes instead of just realloc_pipes + +Cc: Maarten Lankhorst +Cc: Lyude Paul +Cc: Radhakrishna Sripada +Signed-off-by: Matt Roper +Signed-off-by: Maarten Lankhorst +Reviewed-by: Maarten Lankhorst +Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-10-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/i915_drv.h | 5 +++ + drivers/gpu/drm/i915/intel_display.c | 18 ++++++++ + drivers/gpu/drm/i915/intel_drv.h | 3 ++ + drivers/gpu/drm/i915/intel_pm.c | 79 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 105 insertions(+) + +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index e21960d..b908a41 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -339,6 +339,10 @@ struct i915_hotplug { + #define for_each_intel_crtc(dev, intel_crtc) \ + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) + ++#define for_each_intel_crtc_mask(dev, intel_crtc, crtc_mask) \ ++ list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) \ ++ for_each_if ((crtc_mask) & (1 << drm_crtc_index(&intel_crtc->base))) ++ + #define for_each_intel_encoder(dev, intel_encoder) \ + list_for_each_entry(intel_encoder, \ + &(dev)->mode_config.encoder_list, \ +@@ -594,6 +598,7 @@ struct drm_i915_display_funcs { + struct intel_crtc_state *newstate); + void (*initial_watermarks)(struct intel_crtc_state *cstate); + void (*optimize_watermarks)(struct intel_crtc_state *cstate); ++ int (*compute_global_watermarks)(struct drm_atomic_state *state); + void (*update_wm)(struct drm_crtc *crtc); + int (*modeset_calc_cdclk)(struct drm_atomic_state *state); + void (*modeset_commit_cdclk)(struct drm_atomic_state *state); +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index a9d2e30..ecad0ef 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -13342,6 +13342,7 @@ static int intel_modeset_checks(struct drm_atomic_state *state) + static void calc_watermark_data(struct drm_atomic_state *state) + { + struct drm_device *dev = state->dev; ++ struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + struct drm_crtc *crtc; + struct drm_crtc_state *cstate; +@@ -13371,6 +13372,10 @@ static void calc_watermark_data(struct drm_atomic_state *state) + pstate->crtc_h != pstate->src_h >> 16) + intel_state->wm_config.sprites_scaled = true; + } ++ ++ /* Is there platform-specific watermark information to calculate? */ ++ if (dev_priv->display.compute_global_watermarks) ++ dev_priv->display.compute_global_watermarks(state); + } + + /** +@@ -13739,6 +13744,19 @@ static int intel_atomic_commit(struct drm_device *dev, + intel_modeset_verify_crtc(crtc, old_crtc_state, crtc->state); + } + ++ /* ++ * Temporary sanity check: make sure our pre-computed DDB matches the ++ * one we actually wind up programming. ++ * ++ * Not a great place to put this, but the easiest place we have access ++ * to both the pre-computed and final DDB's; we'll be removing this ++ * check in the next patch anyway. ++ */ ++ WARN(IS_GEN9(dev) && ++ memcmp(&intel_state->ddb, &dev_priv->wm.skl_results.ddb, ++ sizeof(intel_state->ddb)), ++ "Pre-computed DDB does not match final DDB!\n"); ++ + if (intel_state->modeset) + intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET); + +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index d19e83e..2218290 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -312,6 +312,9 @@ struct intel_atomic_state { + * don't bother calculating intermediate watermarks. + */ + bool skip_intermediate_wm; ++ ++ /* Gen9+ only */ ++ struct skl_ddb_allocation ddb; + }; + + struct intel_plane_state { +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index a49faa7..cfa4f80 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3812,6 +3812,84 @@ static void skl_clear_wm(struct skl_wm_values *watermarks, enum pipe pipe) + + } + ++static int ++skl_compute_ddb(struct drm_atomic_state *state) ++{ ++ struct drm_device *dev = state->dev; ++ struct drm_i915_private *dev_priv = to_i915(dev); ++ struct intel_atomic_state *intel_state = to_intel_atomic_state(state); ++ struct intel_crtc *intel_crtc; ++ unsigned realloc_pipes = dev_priv->active_crtcs; ++ int ret; ++ ++ /* ++ * If this is our first atomic update following hardware readout, ++ * we can't trust the DDB that the BIOS programmed for us. Let's ++ * pretend that all pipes switched active status so that we'll ++ * ensure a full DDB recompute. ++ */ ++ if (dev_priv->wm.distrust_bios_wm) ++ intel_state->active_pipe_changes = ~0; ++ ++ /* ++ * If the modeset changes which CRTC's are active, we need to ++ * recompute the DDB allocation for *all* active pipes, even ++ * those that weren't otherwise being modified in any way by this ++ * atomic commit. Due to the shrinking of the per-pipe allocations ++ * when new active CRTC's are added, it's possible for a pipe that ++ * we were already using and aren't changing at all here to suddenly ++ * become invalid if its DDB needs exceeds its new allocation. ++ * ++ * Note that if we wind up doing a full DDB recompute, we can't let ++ * any other display updates race with this transaction, so we need ++ * to grab the lock on *all* CRTC's. ++ */ ++ if (intel_state->active_pipe_changes) ++ realloc_pipes = ~0; ++ ++ for_each_intel_crtc_mask(dev, intel_crtc, realloc_pipes) { ++ struct intel_crtc_state *cstate; ++ ++ cstate = intel_atomic_get_crtc_state(state, intel_crtc); ++ if (IS_ERR(cstate)) ++ return PTR_ERR(cstate); ++ ++ ret = skl_allocate_pipe_ddb(cstate, &intel_state->ddb); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++skl_compute_wm(struct drm_atomic_state *state) ++{ ++ struct drm_crtc *crtc; ++ struct drm_crtc_state *cstate; ++ int ret, i; ++ bool changed = false; ++ ++ /* ++ * If this transaction isn't actually touching any CRTC's, don't ++ * bother with watermark calculation. Note that if we pass this ++ * test, we're guaranteed to hold at least one CRTC state mutex, ++ * which means we can safely use values like dev_priv->active_crtcs ++ * since any racing commits that want to update them would need to ++ * hold _all_ CRTC state mutexes. ++ */ ++ for_each_crtc_in_state(state, crtc, cstate, i) ++ changed = true; ++ if (!changed) ++ return 0; ++ ++ ret = skl_compute_ddb(state); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ + static void skl_update_wm(struct drm_crtc *crtc) + { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +@@ -7384,6 +7462,7 @@ void intel_init_pm(struct drm_device *dev) + if (INTEL_INFO(dev)->gen >= 9) { + skl_setup_wm_latency(dev); + dev_priv->display.update_wm = skl_update_wm; ++ dev_priv->display.compute_global_watermarks = skl_compute_wm; + } else if (HAS_PCH_SPLIT(dev)) { + ilk_setup_wm_latency(dev); + +-- +2.7.4 + diff --git a/0010-drm-i915-gen9-Drop-re-allocation-of-DDB-at-atomic-co.patch b/0010-drm-i915-gen9-Drop-re-allocation-of-DDB-at-atomic-co.patch new file mode 100644 index 000000000..26f7e750c --- /dev/null +++ b/0010-drm-i915-gen9-Drop-re-allocation-of-DDB-at-atomic-co.patch @@ -0,0 +1,379 @@ +From a9abdc6767855e1668301a1dcc4b5fa8bed1ddfa Mon Sep 17 00:00:00 2001 +From: Matt Roper +Date: Thu, 12 May 2016 07:06:04 -0700 +Subject: [PATCH 10/17] drm/i915/gen9: Drop re-allocation of DDB at atomic + commit (v2) + +Now that we're properly pre-allocating the DDB during the atomic check +phase and we trust that the allocation is appropriate, let's actually +use the allocation computed and not duplicate that work during the +commit phase. + +v2: + - Significant rebasing now that we can use cached data rates and + minimum block allocations to avoid grabbing additional plane states. + +Signed-off-by: Matt Roper +Reviewed-by: Maarten Lankhorst +Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-11-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_display.c | 14 +-- + drivers/gpu/drm/i915/intel_pm.c | 224 +++++++++++------------------------ + 2 files changed, 67 insertions(+), 171 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index ecad0ef..4db10d7 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -13627,6 +13627,7 @@ static int intel_atomic_commit(struct drm_device *dev, + drm_atomic_helper_swap_state(dev, state); + dev_priv->wm.config = intel_state->wm_config; + dev_priv->wm.distrust_bios_wm = false; ++ dev_priv->wm.skl_results.ddb = intel_state->ddb; + intel_shared_dpll_commit(state); + + if (intel_state->modeset) { +@@ -13744,19 +13745,6 @@ static int intel_atomic_commit(struct drm_device *dev, + intel_modeset_verify_crtc(crtc, old_crtc_state, crtc->state); + } + +- /* +- * Temporary sanity check: make sure our pre-computed DDB matches the +- * one we actually wind up programming. +- * +- * Not a great place to put this, but the easiest place we have access +- * to both the pre-computed and final DDB's; we'll be removing this +- * check in the next patch anyway. +- */ +- WARN(IS_GEN9(dev) && +- memcmp(&intel_state->ddb, &dev_priv->wm.skl_results.ddb, +- sizeof(intel_state->ddb)), +- "Pre-computed DDB does not match final DDB!\n"); +- + if (intel_state->modeset) + intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET); + +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index cfa4f80..0f0d4e1 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -2849,7 +2849,6 @@ skl_wm_plane_id(const struct intel_plane *plane) + static void + skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, + const struct intel_crtc_state *cstate, +- struct intel_wm_config *config, + struct skl_ddb_entry *alloc, /* out */ + int *num_active /* out */) + { +@@ -2857,24 +2856,22 @@ skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, + struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_crtc *for_crtc = cstate->base.crtc; +- struct drm_crtc *crtc; + unsigned int pipe_size, ddb_size; + int nth_active_pipe; + int pipe = to_intel_crtc(for_crtc)->pipe; + +- if (intel_state && intel_state->active_pipe_changes) +- *num_active = hweight32(intel_state->active_crtcs); +- else if (intel_state) +- *num_active = hweight32(dev_priv->active_crtcs); +- else +- *num_active = config->num_pipes_active; +- +- if (!cstate->base.active) { ++ if (WARN_ON(!state) || !cstate->base.active) { + alloc->start = 0; + alloc->end = 0; ++ *num_active = hweight32(dev_priv->active_crtcs); + return; + } + ++ if (intel_state->active_pipe_changes) ++ *num_active = hweight32(intel_state->active_crtcs); ++ else ++ *num_active = hweight32(dev_priv->active_crtcs); ++ + if (IS_BROXTON(dev)) + ddb_size = BXT_DDB_SIZE; + else +@@ -2883,50 +2880,23 @@ skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, + ddb_size -= 4; /* 4 blocks for bypass path allocation */ + + /* +- * FIXME: At the moment we may be called on either in-flight or fully +- * committed cstate's. Once we fully move DDB allocation in the check +- * phase, we'll only be called on in-flight states and the 'else' +- * branch here will go away. +- * +- * The 'else' branch is slightly racy here, but it was racy to begin +- * with; since it's going away soon, no effort is made to address that. ++ * If the state doesn't change the active CRTC's, then there's ++ * no need to recalculate; the existing pipe allocation limits ++ * should remain unchanged. Note that we're safe from racing ++ * commits since any racing commit that changes the active CRTC ++ * list would need to grab _all_ crtc locks, including the one ++ * we currently hold. + */ +- if (state) { +- /* +- * If the state doesn't change the active CRTC's, then there's +- * no need to recalculate; the existing pipe allocation limits +- * should remain unchanged. Note that we're safe from racing +- * commits since any racing commit that changes the active CRTC +- * list would need to grab _all_ crtc locks, including the one +- * we currently hold. +- */ +- if (!intel_state->active_pipe_changes) { +- *alloc = dev_priv->wm.skl_hw.ddb.pipe[pipe]; +- return; +- } +- +- nth_active_pipe = hweight32(intel_state->active_crtcs & +- (drm_crtc_mask(for_crtc) - 1)); +- pipe_size = ddb_size / hweight32(intel_state->active_crtcs); +- alloc->start = nth_active_pipe * ddb_size / *num_active; +- alloc->end = alloc->start + pipe_size; +- } else { +- nth_active_pipe = 0; +- for_each_crtc(dev, crtc) { +- if (!to_intel_crtc(crtc)->active) +- continue; +- +- if (crtc == for_crtc) +- break; +- +- nth_active_pipe++; +- } +- +- pipe_size = ddb_size / config->num_pipes_active; +- alloc->start = nth_active_pipe * ddb_size / +- config->num_pipes_active; +- alloc->end = alloc->start + pipe_size; ++ if (!intel_state->active_pipe_changes) { ++ *alloc = dev_priv->wm.skl_hw.ddb.pipe[pipe]; ++ return; + } ++ ++ nth_active_pipe = hweight32(intel_state->active_crtcs & ++ (drm_crtc_mask(for_crtc) - 1)); ++ pipe_size = ddb_size / hweight32(intel_state->active_crtcs); ++ alloc->start = nth_active_pipe * ddb_size / *num_active; ++ alloc->end = alloc->start + pipe_size; + } + + static unsigned int skl_cursor_allocation(int num_active) +@@ -3025,62 +2995,33 @@ skl_get_total_relative_data_rate(struct intel_crtc_state *intel_cstate) + struct drm_crtc *crtc = cstate->crtc; + struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); ++ const struct drm_plane *plane; + const struct intel_plane *intel_plane; ++ struct drm_plane_state *pstate; + unsigned int rate, total_data_rate = 0; + int id; ++ int i; ++ ++ if (WARN_ON(!state)) ++ return 0; + + /* Calculate and cache data rate for each plane */ +- /* +- * FIXME: At the moment this function can be called on either an +- * in-flight or a committed state object. If it's in-flight then we +- * only want to re-calculate the plane data rate for planes that are +- * part of the transaction (i.e., we don't want to grab any additional +- * plane states if we don't have to). If we're operating on committed +- * state, we'll just go ahead and recalculate the plane data rate for +- * all planes. +- * +- * Once we finish moving our DDB allocation to the atomic check phase, +- * we'll only be calling this function on in-flight state objects, so +- * the 'else' branch here will go away. +- */ +- if (state) { +- struct drm_plane *plane; +- struct drm_plane_state *pstate; +- int i; +- +- for_each_plane_in_state(state, plane, pstate, i) { +- intel_plane = to_intel_plane(plane); +- id = skl_wm_plane_id(intel_plane); +- +- if (intel_plane->pipe != intel_crtc->pipe) +- continue; +- +- /* packed/uv */ +- rate = skl_plane_relative_data_rate(intel_cstate, +- pstate, 0); +- intel_cstate->wm.skl.plane_data_rate[id] = rate; +- +- /* y-plane */ +- rate = skl_plane_relative_data_rate(intel_cstate, +- pstate, 1); +- intel_cstate->wm.skl.plane_y_data_rate[id] = rate; +- } +- } else { +- for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { +- const struct drm_plane_state *pstate = +- intel_plane->base.state; +- int id = skl_wm_plane_id(intel_plane); ++ for_each_plane_in_state(state, plane, pstate, i) { ++ id = skl_wm_plane_id(to_intel_plane(plane)); ++ intel_plane = to_intel_plane(plane); + +- /* packed/uv */ +- rate = skl_plane_relative_data_rate(intel_cstate, +- pstate, 0); +- intel_cstate->wm.skl.plane_data_rate[id] = rate; ++ if (intel_plane->pipe != intel_crtc->pipe) ++ continue; + +- /* y-plane */ +- rate = skl_plane_relative_data_rate(intel_cstate, +- pstate, 1); +- intel_cstate->wm.skl.plane_y_data_rate[id] = rate; +- } ++ /* packed/uv */ ++ rate = skl_plane_relative_data_rate(intel_cstate, ++ pstate, 0); ++ intel_cstate->wm.skl.plane_data_rate[id] = rate; ++ ++ /* y-plane */ ++ rate = skl_plane_relative_data_rate(intel_cstate, ++ pstate, 1); ++ intel_cstate->wm.skl.plane_y_data_rate[id] = rate; + } + + /* Calculate CRTC's total data rate from cached values */ +@@ -3104,8 +3045,6 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + struct drm_atomic_state *state = cstate->base.state; + struct drm_crtc *crtc = cstate->base.crtc; + struct drm_device *dev = crtc->dev; +- struct drm_i915_private *dev_priv = to_i915(dev); +- struct intel_wm_config *config = &dev_priv->wm.config; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_plane *intel_plane; + struct drm_plane *plane; +@@ -3119,6 +3058,9 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + int num_active; + int id, i; + ++ if (WARN_ON(!state)) ++ return 0; ++ + if (!cstate->base.active) { + ddb->pipe[pipe].start = ddb->pipe[pipe].end = 0; + memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe])); +@@ -3126,8 +3068,7 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + return 0; + } + +- skl_ddb_get_pipe_allocation_limits(dev, cstate, config, alloc, +- &num_active); ++ skl_ddb_get_pipe_allocation_limits(dev, cstate, alloc, &num_active); + alloc_size = skl_ddb_entry_size(alloc); + if (alloc_size == 0) { + memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe])); +@@ -3139,53 +3080,31 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate, + ddb->plane[pipe][PLANE_CURSOR].end = alloc->end; + + alloc_size -= cursor_blocks; +- alloc->end -= cursor_blocks; + + /* 1. Allocate the mininum required blocks for each active plane */ +- /* +- * TODO: Remove support for already-committed state once we +- * only allocate DDB on in-flight states. +- */ +- if (state) { +- for_each_plane_in_state(state, plane, pstate, i) { +- intel_plane = to_intel_plane(plane); +- id = skl_wm_plane_id(intel_plane); +- +- if (intel_plane->pipe != pipe) +- continue; +- +- if (!to_intel_plane_state(pstate)->visible) { +- minimum[id] = 0; +- y_minimum[id] = 0; +- continue; +- } +- if (plane->type == DRM_PLANE_TYPE_CURSOR) { +- minimum[id] = 0; +- y_minimum[id] = 0; +- continue; +- } +- +- minimum[id] = 8; +- if (pstate->fb->pixel_format == DRM_FORMAT_NV12) +- y_minimum[id] = 8; +- else +- y_minimum[id] = 0; +- } +- } else { +- for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { +- struct drm_plane *plane = &intel_plane->base; +- struct drm_framebuffer *fb = plane->state->fb; +- int id = skl_wm_plane_id(intel_plane); +- +- if (!to_intel_plane_state(plane->state)->visible) +- continue; ++ for_each_plane_in_state(state, plane, pstate, i) { ++ intel_plane = to_intel_plane(plane); ++ id = skl_wm_plane_id(intel_plane); + +- if (plane->type == DRM_PLANE_TYPE_CURSOR) +- continue; ++ if (intel_plane->pipe != pipe) ++ continue; + +- minimum[id] = 8; +- y_minimum[id] = (fb->pixel_format == DRM_FORMAT_NV12) ? 8 : 0; ++ if (!to_intel_plane_state(pstate)->visible) { ++ minimum[id] = 0; ++ y_minimum[id] = 0; ++ continue; ++ } ++ if (plane->type == DRM_PLANE_TYPE_CURSOR) { ++ minimum[id] = 0; ++ y_minimum[id] = 0; ++ continue; + } ++ ++ minimum[id] = 8; ++ if (pstate->fb->pixel_format == DRM_FORMAT_NV12) ++ y_minimum[id] = 8; ++ else ++ y_minimum[id] = 0; + } + + for (i = 0; i < PLANE_CURSOR; i++) { +@@ -3736,7 +3655,6 @@ static bool skl_update_pipe_wm(struct drm_crtc *crtc, + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); + +- WARN_ON(skl_allocate_pipe_ddb(cstate, ddb) != 0); + skl_build_pipe_wm(cstate, ddb, pipe_wm); + + if (!memcmp(&intel_crtc->wm.active.skl, pipe_wm, sizeof(*pipe_wm))) +@@ -3800,16 +3718,6 @@ static void skl_clear_wm(struct skl_wm_values *watermarks, enum pipe pipe) + memset(watermarks->plane_trans[pipe], + 0, sizeof(uint32_t) * I915_MAX_PLANES); + watermarks->plane_trans[pipe][PLANE_CURSOR] = 0; +- +- /* Clear ddb entries for pipe */ +- memset(&watermarks->ddb.pipe[pipe], 0, sizeof(struct skl_ddb_entry)); +- memset(&watermarks->ddb.plane[pipe], 0, +- sizeof(struct skl_ddb_entry) * I915_MAX_PLANES); +- memset(&watermarks->ddb.y_plane[pipe], 0, +- sizeof(struct skl_ddb_entry) * I915_MAX_PLANES); +- memset(&watermarks->ddb.plane[pipe][PLANE_CURSOR], 0, +- sizeof(struct skl_ddb_entry)); +- + } + + static int +-- +2.7.4 + diff --git a/0011-drm-i915-gen9-Calculate-plane-WM-s-from-state.patch b/0011-drm-i915-gen9-Calculate-plane-WM-s-from-state.patch new file mode 100644 index 000000000..9f839dfa2 --- /dev/null +++ b/0011-drm-i915-gen9-Calculate-plane-WM-s-from-state.patch @@ -0,0 +1,91 @@ +From eacd0ecb98a93e3ff83a4479eadeb9cda05d3579 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 12:41:01 +0200 +Subject: [PATCH 11/17] drm/i915/gen9: Calculate plane WM's from state + +Upstream: since drm-intel-next-2016-05-22 +commit 33815fa55b31a5de4b197c09926ecab3dfb79732 + +Author: Matt Roper +AuthorDate: Thu May 12 07:06:05 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:34:12 2016 -0700 + + drm/i915/gen9: Calculate plane WM's from state + + In a future patch we'll want to calculate plane watermarks for in-flight + atomic state rather than the already-committed state. + + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-12-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_pm.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 80f9f18..3164f30 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3179,16 +3179,14 @@ static bool skl_ddb_allocation_changed(const struct skl_ddb_allocation *new_ddb, + + static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, + struct intel_crtc_state *cstate, +- struct intel_plane *intel_plane, ++ struct intel_plane_state *intel_pstate, + uint16_t ddb_allocation, + int level, + uint16_t *out_blocks, /* out */ + uint8_t *out_lines /* out */) + { +- struct drm_plane *plane = &intel_plane->base; +- struct drm_framebuffer *fb = plane->state->fb; +- struct intel_plane_state *intel_pstate = +- to_intel_plane_state(plane->state); ++ struct drm_plane_state *pstate = &intel_pstate->base; ++ struct drm_framebuffer *fb = pstate->fb; + uint32_t latency = dev_priv->wm.skl_latency[level]; + uint32_t method1, method2; + uint32_t plane_bytes_per_line, plane_blocks_per_line; +@@ -3203,7 +3201,7 @@ static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, + width = drm_rect_width(&intel_pstate->src) >> 16; + height = drm_rect_height(&intel_pstate->src) >> 16; + +- if (intel_rotation_90_or_270(plane->state->rotation)) ++ if (intel_rotation_90_or_270(pstate->rotation)) + swap(width, height); + + cpp = drm_format_plane_cpp(fb->pixel_format, 0); +@@ -3223,7 +3221,7 @@ static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, + fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED) { + uint32_t min_scanlines = 4; + uint32_t y_tile_minimum; +- if (intel_rotation_90_or_270(plane->state->rotation)) { ++ if (intel_rotation_90_or_270(pstate->rotation)) { + int cpp = (fb->pixel_format == DRM_FORMAT_NV12) ? + drm_format_plane_cpp(fb->pixel_format, 1) : + drm_format_plane_cpp(fb->pixel_format, 0); +@@ -3277,17 +3275,19 @@ static void skl_compute_wm_level(const struct drm_i915_private *dev_priv, + struct drm_device *dev = dev_priv->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc); + struct intel_plane *intel_plane; ++ struct intel_plane_state *intel_pstate; + uint16_t ddb_blocks; + enum pipe pipe = intel_crtc->pipe; + + for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { + int i = skl_wm_plane_id(intel_plane); + ++ intel_pstate = to_intel_plane_state(intel_plane->base.state); + ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][i]); + + result->plane_en[i] = skl_compute_plane_wm(dev_priv, + cstate, +- intel_plane, ++ intel_pstate, + ddb_blocks, + level, + &result->plane_res_b[i], +-- +2.7.4 + diff --git a/0012-drm-i915-gen9-Allow-watermark-calculation-on-in-flig.patch b/0012-drm-i915-gen9-Allow-watermark-calculation-on-in-flig.patch new file mode 100644 index 000000000..d0cb50bac --- /dev/null +++ b/0012-drm-i915-gen9-Allow-watermark-calculation-on-in-flig.patch @@ -0,0 +1,156 @@ +From c3d2591095045a8230361d55fadf15ce5dc9127d Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 12:41:12 +0200 +Subject: [PATCH 12/17] drm/i915/gen9: Allow watermark calculation on in-flight + atomic state (v3) + +Upstream: since drm-intel-next-2016-05-22 +commit f4a967523ec7215a3ec867b7ed2e916bd34840e1 + +Author: Matt Roper +AuthorDate: Thu May 12 07:06:06 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:34:23 2016 -0700 + + drm/i915/gen9: Allow watermark calculation on in-flight atomic state (v3) + + In an upcoming patch we'll move this calculation to the atomic 'check' + phase so that the display update can be rejected early if no valid + watermark programming is possible. + + v2: + - Drop intel_pstate_for_cstate_plane() helper and add note about how + the code needs to evolve in the future if we start allowing more than + one pending commit against a CRTC. (Maarten) + + v3: + - Only have skl_compute_wm_level calculate watermarks for enabled + planes; we can just set the other planes on a CRTC to disabled + without having to look at the plane state. This is important because + despite our CRTC lock we can still have racing commits that modify + a disabled plane's property without turning it on. (Maarten) + + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-13-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_pm.c | 61 ++++++++++++++++++++++++++++++++--------- + 1 file changed, 48 insertions(+), 13 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 3164f30..cd29ab6 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3266,23 +3266,56 @@ static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, + return true; + } + +-static void skl_compute_wm_level(const struct drm_i915_private *dev_priv, +- struct skl_ddb_allocation *ddb, +- struct intel_crtc_state *cstate, +- int level, +- struct skl_wm_level *result) ++static int ++skl_compute_wm_level(const struct drm_i915_private *dev_priv, ++ struct skl_ddb_allocation *ddb, ++ struct intel_crtc_state *cstate, ++ int level, ++ struct skl_wm_level *result) + { + struct drm_device *dev = dev_priv->dev; ++ struct drm_atomic_state *state = cstate->base.state; + struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc); ++ struct drm_plane *plane; + struct intel_plane *intel_plane; + struct intel_plane_state *intel_pstate; + uint16_t ddb_blocks; + enum pipe pipe = intel_crtc->pipe; + +- for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) { ++ /* ++ * We'll only calculate watermarks for planes that are actually ++ * enabled, so make sure all other planes are set as disabled. ++ */ ++ memset(result, 0, sizeof(*result)); ++ ++ for_each_intel_plane_mask(dev, intel_plane, cstate->base.plane_mask) { + int i = skl_wm_plane_id(intel_plane); + +- intel_pstate = to_intel_plane_state(intel_plane->base.state); ++ plane = &intel_plane->base; ++ intel_pstate = NULL; ++ if (state) ++ intel_pstate = ++ intel_atomic_get_existing_plane_state(state, ++ intel_plane); ++ ++ /* ++ * Note: If we start supporting multiple pending atomic commits ++ * against the same planes/CRTC's in the future, plane->state ++ * will no longer be the correct pre-state to use for the ++ * calculations here and we'll need to change where we get the ++ * 'unchanged' plane data from. ++ * ++ * For now this is fine because we only allow one queued commit ++ * against a CRTC. Even if the plane isn't modified by this ++ * transaction and we don't have a plane lock, we still have ++ * the CRTC's lock, so we know that no other transactions are ++ * racing with us to update it. ++ */ ++ if (!intel_pstate) ++ intel_pstate = to_intel_plane_state(plane->state); ++ ++ WARN_ON(!intel_pstate->base.fb); ++ + ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][i]); + + result->plane_en[i] = skl_compute_plane_wm(dev_priv, +@@ -3293,6 +3326,8 @@ static void skl_compute_wm_level(const struct drm_i915_private *dev_priv, + &result->plane_res_b[i], + &result->plane_res_l[i]); + } ++ ++ return 0; + } + + static uint32_t +@@ -3587,14 +3622,14 @@ static void skl_flush_wm_values(struct drm_i915_private *dev_priv, + } + } + +-static bool skl_update_pipe_wm(struct drm_crtc *crtc, ++static bool skl_update_pipe_wm(struct drm_crtc_state *cstate, + struct skl_ddb_allocation *ddb, /* out */ + struct skl_pipe_wm *pipe_wm /* out */) + { +- struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +- struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); ++ struct intel_crtc *intel_crtc = to_intel_crtc(cstate->crtc); ++ struct intel_crtc_state *intel_cstate = to_intel_crtc_state(cstate); + +- skl_build_pipe_wm(cstate, ddb, pipe_wm); ++ skl_build_pipe_wm(intel_cstate, ddb, pipe_wm); + + if (!memcmp(&intel_crtc->wm.active.skl, pipe_wm, sizeof(*pipe_wm))) + return false; +@@ -3634,7 +3669,7 @@ static void skl_update_other_pipe_wm(struct drm_device *dev, + if (!intel_crtc->active) + continue; + +- wm_changed = skl_update_pipe_wm(&intel_crtc->base, ++ wm_changed = skl_update_pipe_wm(intel_crtc->base.state, + &r->ddb, &pipe_wm); + + /* +@@ -3752,7 +3787,7 @@ static void skl_update_wm(struct drm_crtc *crtc) + + skl_clear_wm(results, intel_crtc->pipe); + +- if (!skl_update_pipe_wm(crtc, &results->ddb, pipe_wm)) ++ if (!skl_update_pipe_wm(crtc->state, &results->ddb, pipe_wm)) + return; + + skl_compute_wm_results(dev, pipe_wm, results, intel_crtc); +-- +2.7.4 + diff --git a/0013-drm-i915-gen9-Use-a-bitmask-to-track-dirty-pipe-wate.patch b/0013-drm-i915-gen9-Use-a-bitmask-to-track-dirty-pipe-wate.patch new file mode 100644 index 000000000..959860f95 --- /dev/null +++ b/0013-drm-i915-gen9-Use-a-bitmask-to-track-dirty-pipe-wate.patch @@ -0,0 +1,91 @@ +From b43247a865f73fa3b73a878236b5055bfb864169 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 12:41:23 +0200 +Subject: [PATCH 13/17] drm/i915/gen9: Use a bitmask to track dirty pipe + watermarks + +Upstream: since drm-intel-next-2016-05-22 +commit 2b4b9f35d94b1b533bc23110b040b04316480b28 + +Author: Matt Roper +AuthorDate: Thu May 12 07:06:07 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:34:40 2016 -0700 + + drm/i915/gen9: Use a bitmask to track dirty pipe watermarks + + Slightly easier to work with than an array of bools. + + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-14-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/i915_drv.h | 2 +- + drivers/gpu/drm/i915/intel_pm.c | 10 +++++----- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index 237df9f..67c76b6 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -1615,7 +1615,7 @@ struct skl_ddb_allocation { + }; + + struct skl_wm_values { +- bool dirty[I915_MAX_PIPES]; ++ unsigned dirty_pipes; + struct skl_ddb_allocation ddb; + uint32_t wm_linetime[I915_MAX_PIPES]; + uint32_t plane[I915_MAX_PIPES][I915_MAX_PLANES][8]; +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index cd29ab6..611c5a1 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3455,7 +3455,7 @@ static void skl_write_wm_values(struct drm_i915_private *dev_priv, + int i, level, max_level = ilk_wm_max_level(dev); + enum pipe pipe = crtc->pipe; + +- if (!new->dirty[pipe]) ++ if ((new->dirty_pipes & drm_crtc_mask(&crtc->base)) == 0) + continue; + + I915_WRITE(PIPE_WM_LINETIME(pipe), new->wm_linetime[pipe]); +@@ -3680,7 +3680,7 @@ static void skl_update_other_pipe_wm(struct drm_device *dev, + WARN_ON(!wm_changed); + + skl_compute_wm_results(dev, &pipe_wm, r, intel_crtc); +- r->dirty[intel_crtc->pipe] = true; ++ r->dirty_pipes |= drm_crtc_mask(&intel_crtc->base); + } + } + +@@ -3783,7 +3783,7 @@ static void skl_update_wm(struct drm_crtc *crtc) + + + /* Clear all dirty flags */ +- memset(results->dirty, 0, sizeof(bool) * I915_MAX_PIPES); ++ results->dirty_pipes = 0; + + skl_clear_wm(results, intel_crtc->pipe); + +@@ -3791,7 +3791,7 @@ static void skl_update_wm(struct drm_crtc *crtc) + return; + + skl_compute_wm_results(dev, pipe_wm, results, intel_crtc); +- results->dirty[intel_crtc->pipe] = true; ++ results->dirty_pipes |= drm_crtc_mask(&intel_crtc->base); + + skl_update_other_pipe_wm(dev, crtc, results); + skl_write_wm_values(dev_priv, results); +@@ -3952,7 +3952,7 @@ static void skl_pipe_wm_get_hw_state(struct drm_crtc *crtc) + if (!intel_crtc->active) + return; + +- hw->dirty[pipe] = true; ++ hw->dirty_pipes |= drm_crtc_mask(crtc); + + active->linetime = hw->wm_linetime[pipe]; + +-- +2.7.4 + diff --git a/0014-drm-i915-gen9-Propagate-watermark-calculation-failur.patch b/0014-drm-i915-gen9-Propagate-watermark-calculation-failur.patch new file mode 100644 index 000000000..a868ebd1f --- /dev/null +++ b/0014-drm-i915-gen9-Propagate-watermark-calculation-failur.patch @@ -0,0 +1,254 @@ +From 2dda82bdd570042820241e71c02ea36081835f67 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 12:41:35 +0200 +Subject: [PATCH 14/17] drm/i915/gen9: Propagate watermark calculation failures + up the call chain + +Upstream: since drm-intel-next-2016-05-22 +commit 55994c2c38a1101f84cdf277b228f830af8a9c1b + +Author: Matt Roper +AuthorDate: Thu May 12 07:06:08 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:34:48 2016 -0700 + + drm/i915/gen9: Propagate watermark calculation failures up the call chain + + Once we move watermark calculation to the atomic check phase, we'll want + to start rejecting display configurations that exceed out watermark + limits. At the moment we just assume that there's always a valid set of + watermarks, even though this may not actually be true. Let's prepare by + passing return codes up through the call stack in preparation. + + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-15-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_display.c | 10 ++-- + drivers/gpu/drm/i915/intel_pm.c | 90 ++++++++++++++++++++++-------------- + 2 files changed, 61 insertions(+), 39 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index b484fda..9ac2346 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -13222,7 +13222,7 @@ static int intel_modeset_checks(struct drm_atomic_state *state) + * phase. The code here should be run after the per-crtc and per-plane 'check' + * handlers to ensure that all derived state has been updated. + */ +-static void calc_watermark_data(struct drm_atomic_state *state) ++static int calc_watermark_data(struct drm_atomic_state *state) + { + struct drm_device *dev = state->dev; + struct drm_i915_private *dev_priv = to_i915(dev); +@@ -13258,7 +13258,9 @@ static void calc_watermark_data(struct drm_atomic_state *state) + + /* Is there platform-specific watermark information to calculate? */ + if (dev_priv->display.compute_global_watermarks) +- dev_priv->display.compute_global_watermarks(state); ++ return dev_priv->display.compute_global_watermarks(state); ++ ++ return 0; + } + + /** +@@ -13345,9 +13347,7 @@ static int intel_atomic_check(struct drm_device *dev, + return ret; + + intel_fbc_choose_crtc(dev_priv, state); +- calc_watermark_data(state); +- +- return 0; ++ return calc_watermark_data(state); + } + + static int intel_atomic_prepare_commit(struct drm_device *dev, +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 611c5a1..ec22d93 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3177,13 +3177,14 @@ static bool skl_ddb_allocation_changed(const struct skl_ddb_allocation *new_ddb, + return false; + } + +-static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, +- struct intel_crtc_state *cstate, +- struct intel_plane_state *intel_pstate, +- uint16_t ddb_allocation, +- int level, +- uint16_t *out_blocks, /* out */ +- uint8_t *out_lines /* out */) ++static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, ++ struct intel_crtc_state *cstate, ++ struct intel_plane_state *intel_pstate, ++ uint16_t ddb_allocation, ++ int level, ++ uint16_t *out_blocks, /* out */ ++ uint8_t *out_lines, /* out */ ++ bool *enabled /* out */) + { + struct drm_plane_state *pstate = &intel_pstate->base; + struct drm_framebuffer *fb = pstate->fb; +@@ -3195,8 +3196,10 @@ static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, + uint8_t cpp; + uint32_t width = 0, height = 0; + +- if (latency == 0 || !cstate->base.active || !intel_pstate->visible) +- return false; ++ if (latency == 0 || !cstate->base.active || !intel_pstate->visible) { ++ *enabled = false; ++ return 0; ++ } + + width = drm_rect_width(&intel_pstate->src) >> 16; + height = drm_rect_height(&intel_pstate->src) >> 16; +@@ -3257,13 +3260,16 @@ static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, + res_blocks++; + } + +- if (res_blocks >= ddb_allocation || res_lines > 31) +- return false; ++ if (res_blocks >= ddb_allocation || res_lines > 31) { ++ *enabled = false; ++ return 0; ++ } + + *out_blocks = res_blocks; + *out_lines = res_lines; ++ *enabled = true; + +- return true; ++ return 0; + } + + static int +@@ -3281,6 +3287,7 @@ skl_compute_wm_level(const struct drm_i915_private *dev_priv, + struct intel_plane_state *intel_pstate; + uint16_t ddb_blocks; + enum pipe pipe = intel_crtc->pipe; ++ int ret; + + /* + * We'll only calculate watermarks for planes that are actually +@@ -3318,13 +3325,16 @@ skl_compute_wm_level(const struct drm_i915_private *dev_priv, + + ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][i]); + +- result->plane_en[i] = skl_compute_plane_wm(dev_priv, +- cstate, +- intel_pstate, +- ddb_blocks, +- level, +- &result->plane_res_b[i], +- &result->plane_res_l[i]); ++ ret = skl_compute_plane_wm(dev_priv, ++ cstate, ++ intel_pstate, ++ ddb_blocks, ++ level, ++ &result->plane_res_b[i], ++ &result->plane_res_l[i], ++ &result->plane_en[i]); ++ if (ret) ++ return ret; + } + + return 0; +@@ -3361,21 +3371,26 @@ static void skl_compute_transition_wm(struct intel_crtc_state *cstate, + } + } + +-static void skl_build_pipe_wm(struct intel_crtc_state *cstate, +- struct skl_ddb_allocation *ddb, +- struct skl_pipe_wm *pipe_wm) ++static int skl_build_pipe_wm(struct intel_crtc_state *cstate, ++ struct skl_ddb_allocation *ddb, ++ struct skl_pipe_wm *pipe_wm) + { + struct drm_device *dev = cstate->base.crtc->dev; + const struct drm_i915_private *dev_priv = dev->dev_private; + int level, max_level = ilk_wm_max_level(dev); ++ int ret; + + for (level = 0; level <= max_level; level++) { +- skl_compute_wm_level(dev_priv, ddb, cstate, +- level, &pipe_wm->wm[level]); ++ ret = skl_compute_wm_level(dev_priv, ddb, cstate, ++ level, &pipe_wm->wm[level]); ++ if (ret) ++ return ret; + } + pipe_wm->linetime = skl_compute_linetime_wm(cstate); + + skl_compute_transition_wm(cstate, &pipe_wm->trans_wm); ++ ++ return 0; + } + + static void skl_compute_wm_results(struct drm_device *dev, +@@ -3622,21 +3637,27 @@ static void skl_flush_wm_values(struct drm_i915_private *dev_priv, + } + } + +-static bool skl_update_pipe_wm(struct drm_crtc_state *cstate, +- struct skl_ddb_allocation *ddb, /* out */ +- struct skl_pipe_wm *pipe_wm /* out */) ++static int skl_update_pipe_wm(struct drm_crtc_state *cstate, ++ struct skl_ddb_allocation *ddb, /* out */ ++ struct skl_pipe_wm *pipe_wm, /* out */ ++ bool *changed /* out */) + { + struct intel_crtc *intel_crtc = to_intel_crtc(cstate->crtc); + struct intel_crtc_state *intel_cstate = to_intel_crtc_state(cstate); ++ int ret; + +- skl_build_pipe_wm(intel_cstate, ddb, pipe_wm); ++ ret = skl_build_pipe_wm(intel_cstate, ddb, pipe_wm); ++ if (ret) ++ return ret; + + if (!memcmp(&intel_crtc->wm.active.skl, pipe_wm, sizeof(*pipe_wm))) +- return false; ++ *changed = false; ++ else ++ *changed = true; + + intel_crtc->wm.active.skl = *pipe_wm; + +- return true; ++ return 0; + } + + static void skl_update_other_pipe_wm(struct drm_device *dev, +@@ -3669,8 +3690,8 @@ static void skl_update_other_pipe_wm(struct drm_device *dev, + if (!intel_crtc->active) + continue; + +- wm_changed = skl_update_pipe_wm(intel_crtc->base.state, +- &r->ddb, &pipe_wm); ++ skl_update_pipe_wm(intel_crtc->base.state, ++ &r->ddb, &pipe_wm, &wm_changed); + + /* + * If we end up re-computing the other pipe WM values, it's +@@ -3780,14 +3801,15 @@ static void skl_update_wm(struct drm_crtc *crtc) + struct skl_wm_values *results = &dev_priv->wm.skl_results; + struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); + struct skl_pipe_wm *pipe_wm = &cstate->wm.skl.optimal; +- ++ bool wm_changed; + + /* Clear all dirty flags */ + results->dirty_pipes = 0; + + skl_clear_wm(results, intel_crtc->pipe); + +- if (!skl_update_pipe_wm(crtc->state, &results->ddb, pipe_wm)) ++ skl_update_pipe_wm(crtc->state, &results->ddb, pipe_wm, &wm_changed); ++ if (!wm_changed) + return; + + skl_compute_wm_results(dev, pipe_wm, results, intel_crtc); +-- +2.7.4 + diff --git a/0015-drm-i915-gen9-Calculate-watermarks-during-atomic-che.patch b/0015-drm-i915-gen9-Calculate-watermarks-during-atomic-che.patch new file mode 100644 index 000000000..691b6b985 --- /dev/null +++ b/0015-drm-i915-gen9-Calculate-watermarks-during-atomic-che.patch @@ -0,0 +1,302 @@ +From 664f87c5bfcc7798bd5b16e14792f1e9ba2956ea Mon Sep 17 00:00:00 2001 +From: Matt Roper +Date: Thu, 12 May 2016 15:11:40 -0700 +Subject: [PATCH 15/17] drm/i915/gen9: Calculate watermarks during atomic + 'check' (v2) + +Moving watermark calculation into the check phase will allow us to to +reject display configurations for which there are no valid watermark +values before we start trying to program the hardware (although those +tests will come in a subsequent patch). + +Another advantage of moving this calculation to the check phase is that +we can calculate the watermarks in a single shot as part of the atomic +transaction. The watermark interfaces we inherited from our legacy +modesetting days are a bit broken in the atomic design because they use +per-crtc entry points but actually re-calculate and re-program something +that is really more of a global state. That worked okay in the legacy +modesetting world because operations only ever updated a single CRTC at +a time. However in the atomic world, a transaction can involve multiple +CRTC's, which means we wind up computing and programming the watermarks +NxN times (where N is the number of CRTC's involved). With this patch +we eliminate the redundant re-calculation of watermark data for atomic +states (which was the cause of the WARN_ON(!wm_changed) problems that +have plagued us for a while). + +We still need to work on the 'commit' side of watermark handling so that +we aren't doing redundant NxN programming of watermarks, but that's +content for future patches. + +v2: + - Bail out of skl_write_wm_values() if the CRTC isn't active. Now that + we set dirty_pipes to ~0 if the active pipes change (because + we need to deal with DDB changes), we can now wind up here for + disabled pipes, whereas we couldn't before. + +Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=89055 +Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=92181 +Cc: Maarten Lankhorst +Signed-off-by: Matt Roper +Tested-by: Daniel Stone +Reviewed-by: Maarten Lankhorst +Link: http://patchwork.freedesktop.org/patch/msgid/1463091100-13747-1-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_display.c | 2 +- + drivers/gpu/drm/i915/intel_drv.h | 2 +- + drivers/gpu/drm/i915/intel_pm.c | 140 +++++++++++++---------------------- + 3 files changed, 54 insertions(+), 90 deletions(-) + +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index 2190bac..a75daac 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -13627,7 +13627,7 @@ static int intel_atomic_commit(struct drm_device *dev, + drm_atomic_helper_swap_state(dev, state); + dev_priv->wm.config = intel_state->wm_config; + dev_priv->wm.distrust_bios_wm = false; +- dev_priv->wm.skl_results.ddb = intel_state->ddb; ++ dev_priv->wm.skl_results = intel_state->wm_results; + intel_shared_dpll_commit(state); + + if (intel_state->modeset) { +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index 2218290..ab0be7a 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -314,7 +314,7 @@ struct intel_atomic_state { + bool skip_intermediate_wm; + + /* Gen9+ only */ +- struct skl_ddb_allocation ddb; ++ struct skl_wm_values wm_results; + }; + + struct intel_plane_state { +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 342aa66..b072417 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3221,23 +3221,6 @@ static uint32_t skl_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, + return ret; + } + +-static bool skl_ddb_allocation_changed(const struct skl_ddb_allocation *new_ddb, +- const struct intel_crtc *intel_crtc) +-{ +- struct drm_device *dev = intel_crtc->base.dev; +- struct drm_i915_private *dev_priv = dev->dev_private; +- const struct skl_ddb_allocation *cur_ddb = &dev_priv->wm.skl_hw.ddb; +- +- /* +- * If ddb allocation of pipes changed, it may require recalculation of +- * watermarks +- */ +- if (memcmp(new_ddb->pipe, cur_ddb->pipe, sizeof(new_ddb->pipe))) +- return true; +- +- return false; +-} +- + static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, + struct intel_crtc_state *cstate, + struct intel_plane_state *intel_pstate, +@@ -3533,6 +3516,8 @@ static void skl_write_wm_values(struct drm_i915_private *dev_priv, + + if ((new->dirty_pipes & drm_crtc_mask(&crtc->base)) == 0) + continue; ++ if (!crtc->active) ++ continue; + + I915_WRITE(PIPE_WM_LINETIME(pipe), new->wm_linetime[pipe]); + +@@ -3716,66 +3701,9 @@ static int skl_update_pipe_wm(struct drm_crtc_state *cstate, + else + *changed = true; + +- intel_crtc->wm.active.skl = *pipe_wm; +- + return 0; + } + +-static void skl_update_other_pipe_wm(struct drm_device *dev, +- struct drm_crtc *crtc, +- struct skl_wm_values *r) +-{ +- struct intel_crtc *intel_crtc; +- struct intel_crtc *this_crtc = to_intel_crtc(crtc); +- +- /* +- * If the WM update hasn't changed the allocation for this_crtc (the +- * crtc we are currently computing the new WM values for), other +- * enabled crtcs will keep the same allocation and we don't need to +- * recompute anything for them. +- */ +- if (!skl_ddb_allocation_changed(&r->ddb, this_crtc)) +- return; +- +- /* +- * Otherwise, because of this_crtc being freshly enabled/disabled, the +- * other active pipes need new DDB allocation and WM values. +- */ +- for_each_intel_crtc(dev, intel_crtc) { +- struct skl_pipe_wm pipe_wm = {}; +- bool wm_changed; +- +- if (this_crtc->pipe == intel_crtc->pipe) +- continue; +- +- if (!intel_crtc->active) +- continue; +- +- skl_update_pipe_wm(intel_crtc->base.state, +- &r->ddb, &pipe_wm, &wm_changed); +- +- /* +- * If we end up re-computing the other pipe WM values, it's +- * because it was really needed, so we expect the WM values to +- * be different. +- */ +- WARN_ON(!wm_changed); +- +- skl_compute_wm_results(dev, &pipe_wm, r, intel_crtc); +- r->dirty_pipes |= drm_crtc_mask(&intel_crtc->base); +- } +-} +- +-static void skl_clear_wm(struct skl_wm_values *watermarks, enum pipe pipe) +-{ +- watermarks->wm_linetime[pipe] = 0; +- memset(watermarks->plane[pipe], 0, +- sizeof(uint32_t) * 8 * I915_MAX_PLANES); +- memset(watermarks->plane_trans[pipe], +- 0, sizeof(uint32_t) * I915_MAX_PLANES); +- watermarks->plane_trans[pipe][PLANE_CURSOR] = 0; +-} +- + static int + skl_compute_ddb(struct drm_atomic_state *state) + { +@@ -3783,6 +3711,7 @@ skl_compute_ddb(struct drm_atomic_state *state) + struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + struct intel_crtc *intel_crtc; ++ struct skl_ddb_allocation *ddb = &intel_state->wm_results.ddb; + unsigned realloc_pipes = dev_priv->active_crtcs; + int ret; + +@@ -3808,8 +3737,10 @@ skl_compute_ddb(struct drm_atomic_state *state) + * any other display updates race with this transaction, so we need + * to grab the lock on *all* CRTC's. + */ +- if (intel_state->active_pipe_changes) ++ if (intel_state->active_pipe_changes) { + realloc_pipes = ~0; ++ intel_state->wm_results.dirty_pipes = ~0; ++ } + + for_each_intel_crtc_mask(dev, intel_crtc, realloc_pipes) { + struct intel_crtc_state *cstate; +@@ -3818,7 +3749,7 @@ skl_compute_ddb(struct drm_atomic_state *state) + if (IS_ERR(cstate)) + return PTR_ERR(cstate); + +- ret = skl_allocate_pipe_ddb(cstate, &intel_state->ddb); ++ ret = skl_allocate_pipe_ddb(cstate, ddb); + if (ret) + return ret; + } +@@ -3831,8 +3762,11 @@ skl_compute_wm(struct drm_atomic_state *state) + { + struct drm_crtc *crtc; + struct drm_crtc_state *cstate; +- int ret, i; ++ struct intel_atomic_state *intel_state = to_intel_atomic_state(state); ++ struct skl_wm_values *results = &intel_state->wm_results; ++ struct skl_pipe_wm *pipe_wm; + bool changed = false; ++ int ret, i; + + /* + * If this transaction isn't actually touching any CRTC's, don't +@@ -3847,10 +3781,45 @@ skl_compute_wm(struct drm_atomic_state *state) + if (!changed) + return 0; + ++ /* Clear all dirty flags */ ++ results->dirty_pipes = 0; ++ + ret = skl_compute_ddb(state); + if (ret) + return ret; + ++ /* ++ * Calculate WM's for all pipes that are part of this transaction. ++ * Note that the DDB allocation above may have added more CRTC's that ++ * weren't otherwise being modified (and set bits in dirty_pipes) if ++ * pipe allocations had to change. ++ * ++ * FIXME: Now that we're doing this in the atomic check phase, we ++ * should allow skl_update_pipe_wm() to return failure in cases where ++ * no suitable watermark values can be found. ++ */ ++ for_each_crtc_in_state(state, crtc, cstate, i) { ++ struct intel_crtc *intel_crtc = to_intel_crtc(crtc); ++ struct intel_crtc_state *intel_cstate = ++ to_intel_crtc_state(cstate); ++ ++ pipe_wm = &intel_cstate->wm.skl.optimal; ++ ret = skl_update_pipe_wm(cstate, &results->ddb, pipe_wm, ++ &changed); ++ if (ret) ++ return ret; ++ ++ if (changed) ++ results->dirty_pipes |= drm_crtc_mask(crtc); ++ ++ if ((results->dirty_pipes & drm_crtc_mask(crtc)) == 0) ++ /* This pipe's WM's did not change */ ++ continue; ++ ++ intel_cstate->update_wm_pre = true; ++ skl_compute_wm_results(crtc->dev, pipe_wm, results, intel_crtc); ++ } ++ + return 0; + } + +@@ -3862,26 +3831,21 @@ static void skl_update_wm(struct drm_crtc *crtc) + struct skl_wm_values *results = &dev_priv->wm.skl_results; + struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state); + struct skl_pipe_wm *pipe_wm = &cstate->wm.skl.optimal; +- bool wm_changed; +- +- /* Clear all dirty flags */ +- results->dirty_pipes = 0; + +- skl_clear_wm(results, intel_crtc->pipe); +- +- skl_update_pipe_wm(crtc->state, &results->ddb, pipe_wm, &wm_changed); +- if (!wm_changed) ++ if ((results->dirty_pipes & drm_crtc_mask(crtc)) == 0) + return; + +- skl_compute_wm_results(dev, pipe_wm, results, intel_crtc); +- results->dirty_pipes |= drm_crtc_mask(&intel_crtc->base); ++ intel_crtc->wm.active.skl = *pipe_wm; ++ ++ mutex_lock(&dev_priv->wm.wm_mutex); + +- skl_update_other_pipe_wm(dev, crtc, results); + skl_write_wm_values(dev_priv, results); + skl_flush_wm_values(dev_priv, results); + + /* store the new configuration */ + dev_priv->wm.skl_hw = *results; ++ ++ mutex_unlock(&dev_priv->wm.wm_mutex); + } + + static void ilk_compute_wm_config(struct drm_device *dev, +-- +2.7.4 + diff --git a/0016-drm-i915-gen9-Reject-display-updates-that-exceed-wm-.patch b/0016-drm-i915-gen9-Reject-display-updates-that-exceed-wm-.patch new file mode 100644 index 000000000..fb59572bd --- /dev/null +++ b/0016-drm-i915-gen9-Reject-display-updates-that-exceed-wm-.patch @@ -0,0 +1,63 @@ +From 7731c187f1f77501b7dddf419a06c1b42b0f1388 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Mon, 20 Jun 2016 12:42:00 +0200 +Subject: [PATCH 16/17] drm/i915/gen9: Reject display updates that exceed wm + limitations (v2) + +Upstream: since drm-intel-next-2016-05-22 +commit 6b6bada7d476b586d85b1f9df43125804877e09f + +Author: Matt Roper +AuthorDate: Thu May 12 07:06:10 2016 -0700 +Commit: Matt Roper +CommitDate: Fri May 13 07:36:04 2016 -0700 + + drm/i915/gen9: Reject display updates that exceed wm limitations (v2) + + If we can't find any valid level 0 watermark values for the requested + atomic transaction, reject the configuration before we try to start + programming the hardware. + + v2: + - Add extra debugging output when we reject level 0 watermarks so that + we can more easily debug how/why they were rejected. + + Cc: Lyude Paul + Signed-off-by: Matt Roper + Reviewed-by: Maarten Lankhorst + Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-17-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/intel_pm.c | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c +index 73e5242..70dcd2e 100644 +--- a/drivers/gpu/drm/i915/intel_pm.c ++++ b/drivers/gpu/drm/i915/intel_pm.c +@@ -3245,7 +3245,22 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, + + if (res_blocks >= ddb_allocation || res_lines > 31) { + *enabled = false; +- return 0; ++ ++ /* ++ * If there are no valid level 0 watermarks, then we can't ++ * support this display configuration. ++ */ ++ if (level) { ++ return 0; ++ } else { ++ DRM_DEBUG_KMS("Requested display configuration exceeds system watermark limitations\n"); ++ DRM_DEBUG_KMS("Plane %d.%d: blocks required = %u/%u, lines required = %u/31\n", ++ to_intel_crtc(cstate->base.crtc)->pipe, ++ skl_wm_plane_id(to_intel_plane(pstate->plane)), ++ res_blocks, ddb_allocation, res_lines); ++ ++ return -EINVAL; ++ } + } + + *out_blocks = res_blocks; +-- +2.7.4 + diff --git a/0017-drm-i915-Remove-wm_config-from-dev_priv-intel_atomic.patch b/0017-drm-i915-Remove-wm_config-from-dev_priv-intel_atomic.patch new file mode 100644 index 000000000..73a6dacc6 --- /dev/null +++ b/0017-drm-i915-Remove-wm_config-from-dev_priv-intel_atomic.patch @@ -0,0 +1,105 @@ +From 73a35468564f4e47deade0a4a5eb7ec289611ebc Mon Sep 17 00:00:00 2001 +From: Matt Roper +Date: Thu, 12 May 2016 07:06:11 -0700 +Subject: [PATCH 17/17] drm/i915: Remove wm_config from + dev_priv/intel_atomic_state + +We calculate the watermark config into intel_atomic_state and then save +it into dev_priv, but never actually use it from there. This is +left-over from some early ILK-style watermark programming designs that +got changed over time. + +Signed-off-by: Matt Roper +Reviewed-by: Maarten Lankhorst +Link: http://patchwork.freedesktop.org/patch/msgid/1463061971-19638-18-git-send-email-matthew.d.roper@intel.com +--- + drivers/gpu/drm/i915/i915_drv.h | 3 --- + drivers/gpu/drm/i915/intel_display.c | 31 ------------------------------- + drivers/gpu/drm/i915/intel_drv.h | 1 - + 3 files changed, 35 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index e7bde72..608f8e4 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -1961,9 +1961,6 @@ struct drm_i915_private { + */ + uint16_t skl_latency[8]; + +- /* Committed wm config */ +- struct intel_wm_config config; +- + /* + * The skl_wm_values structure is a bit too big for stack + * allocation, so we keep the staging struct where we store +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index a75daac..9c109c6 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -13343,35 +13343,6 @@ static int calc_watermark_data(struct drm_atomic_state *state) + { + struct drm_device *dev = state->dev; + struct drm_i915_private *dev_priv = to_i915(dev); +- struct intel_atomic_state *intel_state = to_intel_atomic_state(state); +- struct drm_crtc *crtc; +- struct drm_crtc_state *cstate; +- struct drm_plane *plane; +- struct drm_plane_state *pstate; +- +- /* +- * Calculate watermark configuration details now that derived +- * plane/crtc state is all properly updated. +- */ +- drm_for_each_crtc(crtc, dev) { +- cstate = drm_atomic_get_existing_crtc_state(state, crtc) ?: +- crtc->state; +- +- if (cstate->active) +- intel_state->wm_config.num_pipes_active++; +- } +- drm_for_each_legacy_plane(plane, dev) { +- pstate = drm_atomic_get_existing_plane_state(state, plane) ?: +- plane->state; +- +- if (!to_intel_plane_state(pstate)->visible) +- continue; +- +- intel_state->wm_config.sprites_enabled = true; +- if (pstate->crtc_w != pstate->src_w >> 16 || +- pstate->crtc_h != pstate->src_h >> 16) +- intel_state->wm_config.sprites_scaled = true; +- } + + /* Is there platform-specific watermark information to calculate? */ + if (dev_priv->display.compute_global_watermarks) +@@ -13625,7 +13596,6 @@ static int intel_atomic_commit(struct drm_device *dev, + } + + drm_atomic_helper_swap_state(dev, state); +- dev_priv->wm.config = intel_state->wm_config; + dev_priv->wm.distrust_bios_wm = false; + dev_priv->wm.skl_results = intel_state->wm_results; + intel_shared_dpll_commit(state); +@@ -15405,7 +15375,6 @@ retry: + } + + /* Write calculated watermark values back */ +- to_i915(dev)->wm.config = to_intel_atomic_state(state)->wm_config; + for_each_crtc_in_state(state, crtc, cstate, i) { + struct intel_crtc_state *cs = to_intel_crtc_state(cstate); + +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index ab0be7a..8d73c20 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -305,7 +305,6 @@ struct intel_atomic_state { + unsigned int min_pixclk[I915_MAX_PIPES]; + + struct intel_shared_dpll_config shared_dpll[I915_NUM_PLLS]; +- struct intel_wm_config wm_config; + + /* + * Current watermarks can't be trusted during hardware readout, so +-- +2.7.4 + diff --git a/ALSA-timer-Fix-leak-in-SNDRV_TIMER_IOCTL_PARAMS.patch b/ALSA-timer-Fix-leak-in-SNDRV_TIMER_IOCTL_PARAMS.patch new file mode 100644 index 000000000..3eb8bf183 --- /dev/null +++ b/ALSA-timer-Fix-leak-in-SNDRV_TIMER_IOCTL_PARAMS.patch @@ -0,0 +1,33 @@ +From 527a5767c165abd2b4dba99da992c51ca7547562 Mon Sep 17 00:00:00 2001 +From: Kangjie Lu +Date: Tue, 3 May 2016 16:44:07 -0400 +Subject: [PATCH 1/3] ALSA: timer: Fix leak in SNDRV_TIMER_IOCTL_PARAMS +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The stack object “tread” has a total size of 32 bytes. Its field +“event” and “val” both contain 4 bytes padding. These 8 bytes +padding bytes are sent to user without being initialized. + +Signed-off-by: Kangjie Lu +Signed-off-by: Takashi Iwai +--- + sound/core/timer.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/core/timer.c b/sound/core/timer.c +index 6469bedda2f3..964f5ebf495e 100644 +--- a/sound/core/timer.c ++++ b/sound/core/timer.c +@@ -1739,6 +1739,7 @@ static int snd_timer_user_params(struct file *file, + if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { + if (tu->tread) { + struct snd_timer_tread tread; ++ memset(&tread, 0, sizeof(tread)); + tread.event = SNDRV_TIMER_EVENT_EARLY; + tread.tstamp.tv_sec = 0; + tread.tstamp.tv_nsec = 0; +-- +2.5.5 + diff --git a/ALSA-timer-Fix-leak-in-events-via-snd_timer_user_cca.patch b/ALSA-timer-Fix-leak-in-events-via-snd_timer_user_cca.patch new file mode 100644 index 000000000..e6f46f8a8 --- /dev/null +++ b/ALSA-timer-Fix-leak-in-events-via-snd_timer_user_cca.patch @@ -0,0 +1,34 @@ +From addd6e9f0e25efb00d813d54528607c75b77c416 Mon Sep 17 00:00:00 2001 +From: Kangjie Lu +Date: Tue, 3 May 2016 16:44:20 -0400 +Subject: [PATCH 2/3] ALSA: timer: Fix leak in events via + snd_timer_user_ccallback +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The stack object “r1” has a total size of 32 bytes. Its field +“event” and “val” both contain 4 bytes padding. These 8 bytes +padding bytes are sent to user without being initialized. + +Signed-off-by: Kangjie Lu +Signed-off-by: Takashi Iwai +--- + sound/core/timer.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/core/timer.c b/sound/core/timer.c +index 964f5ebf495e..e98fa5feb731 100644 +--- a/sound/core/timer.c ++++ b/sound/core/timer.c +@@ -1225,6 +1225,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, + tu->tstamp = *tstamp; + if ((tu->filter & (1 << event)) == 0 || !tu->tread) + return; ++ memset(&r1, 0, sizeof(r1)); + r1.event = event; + r1.tstamp = *tstamp; + r1.val = resolution; +-- +2.5.5 + diff --git a/ALSA-timer-Fix-leak-in-events-via-snd_timer_user_tin.patch b/ALSA-timer-Fix-leak-in-events-via-snd_timer_user_tin.patch new file mode 100644 index 000000000..7851c55a2 --- /dev/null +++ b/ALSA-timer-Fix-leak-in-events-via-snd_timer_user_tin.patch @@ -0,0 +1,34 @@ +From b06a443b5679e9a0298e2f206ddb60845569f62f Mon Sep 17 00:00:00 2001 +From: Kangjie Lu +Date: Tue, 3 May 2016 16:44:32 -0400 +Subject: [PATCH 3/3] ALSA: timer: Fix leak in events via + snd_timer_user_tinterrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The stack object “r1” has a total size of 32 bytes. Its field +“event” and “val” both contain 4 bytes padding. These 8 bytes +padding bytes are sent to user without being initialized. + +Signed-off-by: Kangjie Lu +Signed-off-by: Takashi Iwai +--- + sound/core/timer.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/core/timer.c b/sound/core/timer.c +index e98fa5feb731..c69a27155433 100644 +--- a/sound/core/timer.c ++++ b/sound/core/timer.c +@@ -1268,6 +1268,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, + } + if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && + tu->last_resolution != resolution) { ++ memset(&r1, 0, sizeof(r1)); + r1.event = SNDRV_TIMER_EVENT_RESOLUTION; + r1.tstamp = tstamp; + r1.val = resolution; +-- +2.5.5 + diff --git a/ARM-OMAP4-Fix-crashes.patch b/ARM-OMAP4-Fix-crashes.patch new file mode 100644 index 000000000..57e56e507 --- /dev/null +++ b/ARM-OMAP4-Fix-crashes.patch @@ -0,0 +1,97 @@ +From patchwork Wed Oct 26 15:17:01 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [3/5] ARM: OMAP4+: Fix bad fallthrough for cpuidle +From: Tony Lindgren +X-Patchwork-Id: 9397501 +Message-Id: <20161026151703.24730-4-tony@atomide.com> +To: linux-omap@vger.kernel.org +Cc: Nishanth Menon , Dmitry Lifshitz , + Dave Gerlach , + Enric Balletbo Serra , + "Dr . H . Nikolaus Schaller" , + Pau Pajuel , Grazvydas Ignotas , + Benoit Cousson , + Santosh Shilimkar , + Javier Martinez Canillas , + Robert Nelson , + Marek Belisko , linux-arm-kernel@lists.infradead.org +Date: Wed, 26 Oct 2016 08:17:01 -0700 + +We don't want to fall through to a bunch of errors for retention +if PM_OMAP4_CPU_OSWR_DISABLE is not configured for a SoC. + +Fixes: 6099dd37c669 ("ARM: OMAP5 / DRA7: Enable CPU RET on suspend") +Signed-off-by: Tony Lindgren +--- + arch/arm/mach-omap2/omap-mpuss-lowpower.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c +--- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c ++++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c +@@ -244,10 +244,9 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) + save_state = 1; + break; + case PWRDM_POWER_RET: +- if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) { ++ if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) + save_state = 0; +- break; +- } ++ break; + default: + /* + * CPUx CSWR is invalid hardware state. Also CPUx OSWR +From 5a78ff7bf7e25191144b550961001bbf6c734da4 Mon Sep 17 00:00:00 2001 +From: Peter Chen +Date: Thu, 11 Aug 2016 17:44:54 +0800 +Subject: [PATCH 04152/16809] Revert "gpu: drm: omapdrm: dss-of: add missing + of_node_put after calling of_parse_phandle" + +This reverts +commit 2ab9f5879162499e1c4e48613287e3f59e593c4f +Author: Peter Chen +Date: Fri Jul 15 11:17:03 2016 +0800 + gpu: drm: omapdrm: dss-of: add missing of_node_put after calling of_parse_phandle + +The of_get_next_parent will drop refcount on the passed node, so the reverted +patch is wrong, thanks for Tomi Valkeinen points it. + +Cc: Tomi Valkeinen +Signed-off-by: Peter Chen +Acked-by: Tomi Valkeinen +Signed-off-by: Sean Paul +Link: http://patchwork.freedesktop.org/patch/msgid/1470908694-16362-1-git-send-email-peter.chen@nxp.com +--- + drivers/gpu/drm/omapdrm/dss/dss-of.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/omapdrm/dss/dss-of.c b/drivers/gpu/drm/omapdrm/dss/dss-of.c +index e256d87..dfd4e96 100644 +--- a/drivers/gpu/drm/omapdrm/dss/dss-of.c ++++ b/drivers/gpu/drm/omapdrm/dss/dss-of.c +@@ -125,16 +125,15 @@ u32 dss_of_port_get_port_number(struct device_node *port) + + static struct device_node *omapdss_of_get_remote_port(const struct device_node *node) + { +- struct device_node *np, *np_parent; ++ struct device_node *np; + + np = of_parse_phandle(node, "remote-endpoint", 0); + if (!np) + return NULL; + +- np_parent = of_get_next_parent(np); +- of_node_put(np); ++ np = of_get_next_parent(np); + +- return np_parent; ++ return np; + } + + struct device_node * +-- +2.9.3 + diff --git a/ARM-mvebu-change-order-of-ethernet-DT-nodes-on-Armada-38x.patch b/ARM-mvebu-change-order-of-ethernet-DT-nodes-on-Armada-38x.patch new file mode 100644 index 000000000..cff3d3339 --- /dev/null +++ b/ARM-mvebu-change-order-of-ethernet-DT-nodes-on-Armada-38x.patch @@ -0,0 +1,101 @@ +From patchwork Wed Jan 27 15:08:19 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [1/2] ARM: mvebu: change order of ethernet DT nodes on Armada 38x +From: Thomas Petazzoni +X-Patchwork-Id: 8134751 +Message-Id: <1453907300-28283-2-git-send-email-thomas.petazzoni@free-electrons.com> +To: Jason Cooper , Andrew Lunn , + Sebastian Hesselbarth , + Gregory Clement +Cc: Nadav Haklai , Lior Amsalem , + Thomas Petazzoni , + linux-arm-kernel@lists.infradead.org +Date: Wed, 27 Jan 2016 16:08:19 +0100 + +On Armada 38x, the available network interfaces are: + + - port 0, at 0x70000 + - port 1, at 0x30000 + - port 2, at 0x34000 + +Due to the rule saying that DT nodes should be ordered by register +addresses, the network interfaces are probed in this order: + + - port 1, at 0x30000, which gets named eth0 + - port 2, at 0x34000, which gets named eth1 + - port 0, at 0x70000, which gets named eth2 + +(if all three ports are enabled at the board level) + +Unfortunately, the network subsystem doesn't provide any way to rename +network interfaces from the kernel (it can only be done from +userspace). So, the default naming of the network interfaces is very +confusing as it doesn't match the datasheet, nor the naming of the +interfaces in the bootloader, nor the naming of the interfaces on +labels printed on the board. + +For example, on the Armada 388 GP, the board has two ports, labelled +GE0 and GE1. One has to know that GE0 is eth1 and GE1 is eth0, which +isn't really obvious. + +In order to solve this, this patch proposes to exceptionaly violate +the rule of "order DT nodes by register address", and put the 0x70000 +node before the 0x30000 node, so that network interfaces get named in +a more natural way. + +Signed-off-by: Thomas Petazzoni + +--- +arch/arm/boot/dts/armada-38x.dtsi | 30 +++++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi +index e8b7f67..b50784d 100644 +--- a/arch/arm/boot/dts/armada-38x.dtsi ++++ b/arch/arm/boot/dts/armada-38x.dtsi +@@ -429,6 +429,27 @@ + reg = <0x22000 0x1000>; + }; + ++ /* ++ * As a special exception to the "order by ++ * register address" rule, the eth0 node is ++ * placed here to ensure that it gets ++ * registered as the first interface, since ++ * the network subsystem doesn't allow naming ++ * interfaces using DT aliases. Without this, ++ * the ordering of interfaces is different ++ * from the one used in U-Boot and the ++ * labeling of interfaces on the boards, which ++ * is very confusing for users. ++ */ ++ eth0: ethernet@70000 { ++ compatible = "marvell,armada-370-neta"; ++ reg = <0x70000 0x4000>; ++ interrupts-extended = <&mpic 8>; ++ clocks = <&gateclk 4>; ++ tx-csum-limit = <9800>; ++ status = "disabled"; ++ }; ++ + eth1: ethernet@30000 { + compatible = "marvell,armada-370-neta"; + reg = <0x30000 0x4000>; +@@ -493,15 +514,6 @@ + }; + }; + +- eth0: ethernet@70000 { +- compatible = "marvell,armada-370-neta"; +- reg = <0x70000 0x4000>; +- interrupts-extended = <&mpic 8>; +- clocks = <&gateclk 4>; +- tx-csum-limit = <9800>; +- status = "disabled"; +- }; +- + mdio: mdio@72004 { + #address-cells = <1>; + #size-cells = <0>; diff --git a/AllWinner-net-emac.patch b/AllWinner-net-emac.patch new file mode 100644 index 000000000..7bcaecf4a --- /dev/null +++ b/AllWinner-net-emac.patch @@ -0,0 +1,2700 @@ +From e90ba04ee5efbed6fc17dfa7f62702f268fc1bc4 Mon Sep 17 00:00:00 2001 +From: Corentin LABBE +Date: Fri, 7 Oct 2016 10:25:48 +0200 +Subject: [PATCH 1/9] ethernet: add sun8i-emac driver + +This patch add support for sun8i-emac ethernet MAC hardware. +It could be found in Allwinner H3/A83T/A64 SoCs. + +It supports 10/100/1000 Mbit/s speed with half/full duplex. +It can use an internal PHY (MII 10/100) or an external PHY +via RGMII/RMII. + +Signed-off-by: Corentin Labbe +--- + drivers/net/ethernet/allwinner/Kconfig | 13 + + drivers/net/ethernet/allwinner/Makefile | 1 + + drivers/net/ethernet/allwinner/sun8i-emac.c | 2266 +++++++++++++++++++++++++++ + 3 files changed, 2280 insertions(+) + create mode 100644 drivers/net/ethernet/allwinner/sun8i-emac.c + +diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig +index 47da7e7..060569c 100644 +--- a/drivers/net/ethernet/allwinner/Kconfig ++++ b/drivers/net/ethernet/allwinner/Kconfig +@@ -33,4 +33,17 @@ config SUN4I_EMAC + To compile this driver as a module, choose M here. The module + will be called sun4i-emac. + ++config SUN8I_EMAC ++ tristate "Allwinner sun8i EMAC support" ++ depends on ARCH_SUNXI || COMPILE_TEST ++ depends on OF ++ select MII ++ select PHYLIB ++ ---help--- ++ This driver support the sun8i EMAC ethernet driver present on ++ H3/A83T/A64 Allwinner SoCs. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called sun8i-emac. ++ + endif # NET_VENDOR_ALLWINNER +diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile +index 03129f7..8bd1693c 100644 +--- a/drivers/net/ethernet/allwinner/Makefile ++++ b/drivers/net/ethernet/allwinner/Makefile +@@ -3,3 +3,4 @@ + # + + obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o ++obj-$(CONFIG_SUN8I_EMAC) += sun8i-emac.o +diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers/net/ethernet/allwinner/sun8i-emac.c +new file mode 100644 +index 0000000..bc74467 +--- /dev/null ++++ b/drivers/net/ethernet/allwinner/sun8i-emac.c +@@ -0,0 +1,2266 @@ ++/* ++ * sun8i-emac driver ++ * ++ * Copyright (C) 2015-2016 Corentin LABBE ++ * ++ * This is the driver for Allwinner Ethernet MAC found in H3/A83T/A64 SoC ++ * ++ * TODO: ++ * - MAC filtering ++ * - Jumbo frame ++ * - features rx-all (NETIF_F_RXALL_BIT) ++ * - PM runtime ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define EMAC_BASIC_CTL0 0x00 ++#define EMAC_BASIC_CTL1 0x04 ++#define EMAC_INT_STA 0x08 ++#define EMAC_INT_EN 0x0C ++#define EMAC_TX_CTL0 0x10 ++#define EMAC_TX_CTL1 0x14 ++#define EMAC_TX_FLOW_CTL 0x1C ++#define EMAC_RX_CTL0 0x24 ++#define EMAC_RX_CTL1 0x28 ++#define EMAC_RX_FRM_FLT 0x38 ++#define EMAC_MDIO_CMD 0x48 ++#define EMAC_MDIO_DATA 0x4C ++#define EMAC_TX_DMA_STA 0xB0 ++#define EMAC_TX_CUR_DESC 0xB4 ++#define EMAC_TX_CUR_BUF 0xB8 ++#define EMAC_RX_DMA_STA 0xC0 ++ ++#define MDIO_CMD_MII_BUSY BIT(0) ++#define MDIO_CMD_MII_WRITE BIT(1) ++#define MDIO_CMD_MII_PHY_REG_ADDR_MASK GENMASK(8, 4) ++#define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4 ++#define MDIO_CMD_MII_PHY_ADDR_MASK GENMASK(16, 12) ++#define MDIO_CMD_MII_PHY_ADDR_SHIFT 12 ++ ++#define EMAC_MACADDR_HI 0x50 ++#define EMAC_MACADDR_LO 0x54 ++ ++#define EMAC_RX_DESC_LIST 0x34 ++#define EMAC_TX_DESC_LIST 0x20 ++ ++#define EMAC_RX_DO_CRC BIT(27) ++#define EMAC_RX_STRIP_FCS BIT(28) ++ ++#define LE32_BIT(x) (cpu_to_le32(BIT(x))) ++ ++#define EMAC_COULD_BE_USED_BY_DMA LE32_BIT(31) ++ ++/* Used in RX_CTL1*/ ++#define EMAC_RX_DMA_EN BIT(30) ++#define EMAC_RX_DMA_START BIT(31) ++/* Used in TX_CTL1*/ ++#define EMAC_TX_DMA_EN BIT(30) ++#define EMAC_TX_DMA_START BIT(31) ++ ++/* Used in RX_CTL0 */ ++#define EMAC_RX_RECEIVER_EN BIT(31) ++/* Used in TX_CTL0 */ ++#define EMAC_TX_TRANSMITTER_EN BIT(31) ++ ++/* Basic CTL0 */ ++#define EMAC_BCTL0_FD BIT(0) ++#define EMAC_BCTL0_SPEED_10 2 ++#define EMAC_BCTL0_SPEED_100 3 ++#define EMAC_BCTL0_SPEED_MASK GENMASK(3, 2) ++#define EMAC_BCTL0_SPEED_SHIFT 2 ++ ++#define EMAC_FLOW_RX 1 ++#define EMAC_FLOW_TX 2 ++ ++#define EMAC_TX_INT BIT(0) ++#define EMAC_TX_DMA_STOP_INT BIT(1) ++#define EMAC_TX_BUF_UA_INT BIT(2) ++#define EMAC_TX_TIMEOUT_INT BIT(3) ++#define EMAC_TX_UNDERFLOW_INT BIT(4) ++#define EMAC_TX_EARLY_INT BIT(5) ++#define EMAC_RX_INT BIT(8) ++#define EMAC_RX_BUF_UA_INT BIT(9) ++#define EMAC_RX_DMA_STOP_INT BIT(10) ++#define EMAC_RX_TIMEOUT_INT BIT(11) ++#define EMAC_RX_OVERFLOW_INT BIT(12) ++#define EMAC_RX_EARLY_INT BIT(13) ++#define EMAC_RGMII_STA_INT BIT(16) ++ ++/* Bits used in frame RX status */ ++#define EMAC_DSC_RX_FIRST BIT(9) ++#define EMAC_DSC_RX_LAST BIT(8) ++ ++/* Bits used in frame TX ctl */ ++#define EMAC_MAGIC_TX_BIT LE32_BIT(24) ++#define EMAC_TX_DO_CRC (LE32_BIT(27) | LE32_BIT(28)) ++#define EMAC_DSC_TX_FIRST LE32_BIT(29) ++#define EMAC_DSC_TX_LAST LE32_BIT(30) ++#define EMAC_WANT_INT LE32_BIT(31) ++ ++/* struct emac_variant - Describe an emac variant of sun8i-emac ++ * @default_syscon_value: Default value of the syscon EMAC register ++ * The default_syscon_value is also used for powering down the PHY ++ * @internal_phy: which PHY type is internal ++ * @support_mii: Does the SoC support MII ++ * @support_rmii: Does the SoC support RMII ++ * @support_rgmii: Does the SoC support RGMII ++ */ ++struct emac_variant { ++ u32 default_syscon_value; ++ int internal_phy; ++ bool support_mii; ++ bool support_rmii; ++ bool support_rgmii; ++}; ++ ++static const struct emac_variant emac_variant_h3 = { ++ .default_syscon_value = 0x58000, ++ .internal_phy = PHY_INTERFACE_MODE_MII, ++ .support_mii = true, ++ .support_rmii = true, ++ .support_rgmii = true ++}; ++ ++static const struct emac_variant emac_variant_a83t = { ++ .default_syscon_value = 0, ++ .internal_phy = 0, ++ .support_mii = true, ++ .support_rgmii = true ++}; ++ ++static const struct emac_variant emac_variant_a64 = { ++ .default_syscon_value = 0, ++ .internal_phy = 0, ++ .support_mii = true, ++ .support_rmii = true, ++ .support_rgmii = true ++}; ++ ++static const char const estats_str[][ETH_GSTRING_LEN] = { ++ /* errors */ ++ "rx_payload_error", ++ "rx_CRC_error", ++ "rx_phy_error", ++ "rx_length_error", ++ "rx_col_error", ++ "rx_header_error", ++ "rx_overflow_error", ++ "rx_saf_error", ++ "rx_daf_error", ++ "rx_buf_error", ++ "rx_invalid_error", ++ "tx_timeout", ++ /* misc infos */ ++ "tx_stop_queue", ++ "rx_dma_ua", ++ "rx_dma_stop", ++ "tx_dma_ua", ++ "tx_dma_stop", ++ "rx_hw_csum", ++ "tx_hw_csum", ++ /* interrupts */ ++ "rx_int", ++ "tx_int", ++ "tx_early_int", ++ "tx_underflow_int", ++ "tx_timeout_int", ++ "rx_early_int", ++ "rx_overflow_int", ++ "rx_timeout_int", ++ "rgmii_state_int", ++ /* debug */ ++ "tx_used_desc", ++ "napi_schedule", ++ "napi_underflow", ++}; ++ ++struct sun8i_emac_stats { ++ u64 rx_payload_error; ++ u64 rx_crc_error; ++ u64 rx_phy_error; ++ u64 rx_length_error; ++ u64 rx_col_error; ++ u64 rx_header_error; ++ u64 rx_overflow_error; ++ u64 rx_saf_fail; ++ u64 rx_daf_fail; ++ u64 rx_buf_error; ++ u64 rx_invalid_error; ++ u64 tx_timeout; ++ ++ u64 tx_stop_queue; ++ u64 rx_dma_ua; ++ u64 rx_dma_stop; ++ u64 tx_dma_ua; ++ u64 tx_dma_stop; ++ u64 rx_hw_csum; ++ u64 tx_hw_csum; ++ ++ u64 rx_int; ++ u64 tx_int; ++ u64 tx_early_int; ++ u64 tx_underflow_int; ++ u64 tx_timeout_int; ++ u64 rx_early_int; ++ u64 rx_overflow_int; ++ u64 rx_timeout_int; ++ u64 rgmii_state_int; ++ ++ u64 tx_used_desc; ++ u64 napi_schedule; ++ u64 napi_underflow; ++}; ++ ++/* The datasheet said that each descriptor can transfers up to 4096bytes ++ * But latter, a register documentation reduce that value to 2048 ++ * Anyway using 2048 cause strange behaviours and even BSP driver use 2047 ++ */ ++#define DESC_BUF_MAX 2044 ++ ++/* MAGIC value for knowing if a descriptor is available or not */ ++#define DCLEAN cpu_to_le32(BIT(16) | BIT(14) | BIT(12) | BIT(10) | BIT(9)) ++ ++/* struct dma_desc - Structure of DMA descriptor used by the hardware ++ * @status: Status of the frame written by HW, so RO for the ++ * driver (except for BIT(31) which is R/W) ++ * @ctl: Information on the frame written by the driver (INT, len,...) ++ * @buf_addr: physical address of the frame data ++ * @next: physical address of next dma_desc ++ */ ++struct dma_desc { ++ __le32 status; ++ __le32 ctl; ++ __le32 buf_addr; ++ __le32 next; ++}; ++ ++/* Describe how data from skb are DMA mapped (used in txinfo map member) */ ++#define MAP_SINGLE 1 ++#define MAP_PAGE 2 ++ ++/* Structure for storing information about data in TX ring buffer */ ++struct txinfo { ++ struct sk_buff *skb; ++ int map; ++}; ++ ++struct sun8i_emac_priv { ++ void __iomem *base; ++ struct regmap *regmap; ++ int irq; ++ struct device *dev; ++ struct net_device *ndev; ++ struct mii_bus *mdio; ++ struct napi_struct napi; ++ spinlock_t tx_lock;/* control the access of transmit descriptors */ ++ int duplex; ++ int speed; ++ int link; ++ int phy_interface; ++ const struct emac_variant *variant; ++ struct device_node *phy_node; ++ struct device_node *mdio_node; ++ struct clk *ahb_clk; ++ struct clk *ephy_clk; ++ bool use_internal_phy; ++ ++ struct reset_control *rst_mac; ++ struct reset_control *rst_ephy; ++ ++ struct dma_desc *dd_rx; ++ dma_addr_t dd_rx_phy; ++ struct dma_desc *dd_tx; ++ dma_addr_t dd_tx_phy; ++ struct sk_buff **rx_skb; ++ struct txinfo *txl; ++ ++ int nbdesc_tx; ++ int nbdesc_rx; ++ int tx_slot; ++ int tx_dirty; ++ int rx_dirty; ++ struct sun8i_emac_stats estats; ++ u32 msg_enable; ++ int flow_ctrl; ++ int pause; ++}; ++ ++static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id); ++ ++static void rb_inc(int *p, const int max) ++{ ++ (*p)++; ++ (*p) %= max; ++} ++ ++/* Locking strategy: ++ * RX queue does not need any lock since only sun8i_emac_poll() access it. ++ * (All other RX modifiers (ringparam/ndo_stop) disable NAPI and so ++ * sun8i_emac_poll()) ++ * TX queue is handled by sun8i_emac_xmit(), sun8i_emac_complete_xmit() and ++ * sun8i_emac_tx_timeout() ++ * (All other RX modifiers (ringparam/ndo_stop) disable NAPI and stop queue) ++ * ++ * sun8i_emac_xmit() could fire only once (netif_tx_lock) ++ * sun8i_emac_complete_xmit() could fire only once (called from NAPI) ++ * sun8i_emac_tx_timeout() could fire only once (netif_tx_lock) and could not ++ * race with sun8i_emac_xmit (due to netif_tx_lock) and with ++ * sun8i_emac_complete_xmit which disable NAPI. ++ * ++ * So only sun8i_emac_xmit and sun8i_emac_complete_xmit could fire at the same ++ * time. ++ * But they never could modify the same descriptors: ++ * - sun8i_emac_complete_xmit() will modify only descriptors with empty status ++ * - sun8i_emac_xmit() will modify only descriptors set to DCLEAN ++ * Proper memory barriers ensure that descriptor set to DCLEAN could not be ++ * modified latter by sun8i_emac_complete_xmit(). ++ */ ++ ++/* Return the number of contiguous free descriptors ++ * starting from tx_slot ++ */ ++static int rb_tx_numfreedesc(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ if (priv->tx_slot < priv->tx_dirty) ++ return priv->tx_dirty - priv->tx_slot; ++ ++ return (priv->nbdesc_tx - priv->tx_slot) + priv->tx_dirty; ++} ++ ++/* sun8i_emac_rx_skb - Allocate a skb in a DMA descriptor ++ * ++ * @ndev: The net_device for this interface ++ * @i: index of slot to fill ++ * ++ * Refill a DMA descriptor with a fresh skb and map it for DMA. ++*/ ++static int sun8i_emac_rx_skb(struct net_device *ndev, int i) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc; ++ struct sk_buff *skb; ++ ++ ddesc = priv->dd_rx + i; ++ ++ ddesc->ctl = 0; ++ ++ skb = netdev_alloc_skb_ip_align(ndev, DESC_BUF_MAX); ++ if (!skb) ++ return -ENOMEM; ++ ++ /* should not happen */ ++ if (unlikely(priv->rx_skb[i])) ++ dev_warn(priv->dev, "BUG: Leaking a skbuff\n"); ++ ++ priv->rx_skb[i] = skb; ++ ++ ddesc->buf_addr = dma_map_single(priv->dev, skb->data, ++ DESC_BUF_MAX, DMA_FROM_DEVICE); ++ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { ++ dev_err(priv->dev, "ERROR: Cannot map RX buffer for DMA\n"); ++ dev_kfree_skb(skb); ++ return -EFAULT; ++ } ++ /* We cannot direcly use cpu_to_le32() after dma_map_single ++ * since dma_mapping_error use it ++ */ ++ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); ++ ddesc->ctl |= cpu_to_le32(DESC_BUF_MAX); ++ /* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ ++ wmb(); ++ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; ++ ++ return 0; ++} ++ ++static void sun8i_emac_stop_tx(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ netif_stop_queue(ndev); ++ ++ v = readl(priv->base + EMAC_TX_CTL0); ++ /* Disable transmitter after current reception */ ++ v &= ~EMAC_TX_TRANSMITTER_EN; ++ writel(v, priv->base + EMAC_TX_CTL0); ++ ++ v = readl(priv->base + EMAC_TX_CTL1); ++ /* Stop TX DMA */ ++ v &= ~EMAC_TX_DMA_EN; ++ writel(v, priv->base + EMAC_TX_CTL1); ++ ++ /* We must be sure that all is stopped before leaving this function */ ++ wmb(); ++} ++ ++static void sun8i_emac_stop_rx(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ v = readl(priv->base + EMAC_RX_CTL0); ++ /* Disable receiver after current reception */ ++ v &= ~EMAC_RX_RECEIVER_EN; ++ writel(v, priv->base + EMAC_RX_CTL0); ++ ++ v = readl(priv->base + EMAC_RX_CTL1); ++ /* Stop RX DMA */ ++ v &= ~EMAC_RX_DMA_EN; ++ writel(v, priv->base + EMAC_RX_CTL1); ++ ++ /* We must be sure that all is stopped before leaving this function */ ++ wmb(); ++} ++ ++static void sun8i_emac_start_rx(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ v = readl(priv->base + EMAC_RX_CTL0); ++ /* Enable receiver */ ++ v |= EMAC_RX_RECEIVER_EN; ++ writel(v, priv->base + EMAC_RX_CTL0); ++ ++ v = readl(priv->base + EMAC_RX_CTL1); ++ v |= EMAC_RX_DMA_START; ++ v |= EMAC_RX_DMA_EN; ++ writel(v, priv->base + EMAC_RX_CTL1); ++} ++ ++static void sun8i_emac_start_tx(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ v = readl(priv->base + EMAC_TX_CTL0); ++ v |= EMAC_TX_TRANSMITTER_EN; ++ writel(v, priv->base + EMAC_TX_CTL0); ++ ++ v = readl(priv->base + EMAC_TX_CTL1); ++ v |= EMAC_TX_DMA_START; ++ v |= EMAC_TX_DMA_EN; ++ writel(v, priv->base + EMAC_TX_CTL1); ++} ++ ++/* sun8i_emac_set_macaddr - Set MAC address for slot index ++ * ++ * @addr: the MAC address to set ++ * @index: The index of slot where to set address. ++ * ++ * The slot 0 is the main MAC address ++ */ ++static void sun8i_emac_set_macaddr(struct sun8i_emac_priv *priv, ++ const u8 *addr, int index) ++{ ++ u32 v; ++ ++ dev_info(priv->dev, "device MAC address slot %d %pM", index, addr); ++ ++ v = (addr[5] << 8) | addr[4]; ++ writel(v, priv->base + EMAC_MACADDR_HI + index * 8); ++ ++ v = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; ++ writel(v, priv->base + EMAC_MACADDR_LO + index * 8); ++} ++ ++static void sun8i_emac_set_link_mode(struct sun8i_emac_priv *priv) ++{ ++ u32 v; ++ ++ v = readl(priv->base + EMAC_BASIC_CTL0); ++ ++ if (priv->duplex) ++ v |= EMAC_BCTL0_FD; ++ else ++ v &= ~EMAC_BCTL0_FD; ++ ++ v &= ~EMAC_BCTL0_SPEED_MASK; ++ ++ switch (priv->speed) { ++ case 1000: ++ break; ++ case 100: ++ v |= EMAC_BCTL0_SPEED_100 << EMAC_BCTL0_SPEED_SHIFT; ++ break; ++ case 10: ++ v |= EMAC_BCTL0_SPEED_10 << EMAC_BCTL0_SPEED_SHIFT; ++ break; ++ default: ++ dev_err(priv->dev, "Unsupported speed %d\n", priv->speed); ++ return; ++ } ++ ++ writel(v, priv->base + EMAC_BASIC_CTL0); ++} ++ ++static void sun8i_emac_flow_ctrl(struct sun8i_emac_priv *priv, int duplex, ++ int fc) ++{ ++ u32 flow = 0; ++ ++ flow = readl(priv->base + EMAC_RX_CTL0); ++ if (fc & EMAC_FLOW_RX) ++ flow |= BIT(16); ++ else ++ flow &= ~BIT(16); ++ writel(flow, priv->base + EMAC_RX_CTL0); ++ ++ flow = readl(priv->base + EMAC_TX_FLOW_CTL); ++ if (fc & EMAC_FLOW_TX) ++ flow |= BIT(0); ++ else ++ flow &= ~BIT(0); ++ writel(flow, priv->base + EMAC_TX_FLOW_CTL); ++} ++ ++/* Grab a frame into a skb from descriptor number i */ ++static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i) ++{ ++ struct sk_buff *skb; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc = priv->dd_rx + i; ++ int frame_len; ++ int rxcsum_done = 0; ++ u32 dstatus = le32_to_cpu(ddesc->status); ++ ++ if (ndev->features & NETIF_F_RXCSUM) ++ rxcsum_done = 1; ++ ++ /* bit0/bit7 work only on IPv4/IPv6 TCP traffic, ++ * (not on ARP for example) so we do not raise rx_errors/discard frame ++ */ ++ /* the checksum or length of received frame's payload is wrong*/ ++ if (dstatus & BIT(0)) { ++ priv->estats.rx_payload_error++; ++ rxcsum_done = 0; ++ } ++ ++ /* RX_CRC_ERR */ ++ if (dstatus & BIT(1)) { ++ priv->ndev->stats.rx_errors++; ++ priv->ndev->stats.rx_crc_errors++; ++ priv->estats.rx_crc_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_PHY_ERR */ ++ if ((dstatus & BIT(3))) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_phy_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_LENGTH_ERR */ ++ if ((dstatus & BIT(4))) { ++ priv->ndev->stats.rx_errors++; ++ priv->ndev->stats.rx_length_errors++; ++ priv->estats.rx_length_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_COL_ERR */ ++ if ((dstatus & BIT(6))) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_col_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_HEADER_ERR */ ++ if ((dstatus & BIT(7))) { ++ priv->estats.rx_header_error++; ++ rxcsum_done = 0; ++ } ++ ++ /* RX_OVERFLOW_ERR */ ++ if ((dstatus & BIT(11))) { ++ priv->ndev->stats.rx_over_errors++; ++ priv->estats.rx_overflow_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_NO_ENOUGTH_BUF_ERR */ ++ if ((dstatus & BIT(14))) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_buf_error++; ++ goto discard_frame; ++ } ++ ++ /* BIT(9) is for the first frame, not having it is bad since we do not ++ * handle Jumbo frame ++ */ ++ if ((dstatus & EMAC_DSC_RX_FIRST) == 0) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_invalid_error++; ++ goto discard_frame; ++ } ++ ++ /* this frame is not the last */ ++ if ((dstatus & EMAC_DSC_RX_LAST) == 0) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_invalid_error++; ++ goto discard_frame; ++ } ++ ++ frame_len = (dstatus >> 16) & 0x3FFF; ++ if (!(ndev->features & NETIF_F_RXFCS)) ++ frame_len -= ETH_FCS_LEN; ++ ++ skb = priv->rx_skb[i]; ++ ++ netif_dbg(priv, rx_status, priv->ndev, ++ "%s from %02d %pad len=%d status=%x st=%x\n", ++ __func__, i, &ddesc, frame_len, dstatus, ++ cpu_to_le32(ddesc->ctl)); ++ ++ skb_put(skb, frame_len); ++ ++ dma_unmap_single(priv->dev, le32_to_cpu(ddesc->buf_addr), DESC_BUF_MAX, ++ DMA_FROM_DEVICE); ++ skb->protocol = eth_type_trans(skb, priv->ndev); ++ if (rxcsum_done) { ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ priv->estats.rx_hw_csum++; ++ } else { ++ skb->ip_summed = CHECKSUM_PARTIAL; ++ } ++ ++ priv->ndev->stats.rx_packets++; ++ priv->ndev->stats.rx_bytes += frame_len; ++ priv->rx_skb[i] = NULL; ++ ++ sun8i_emac_rx_skb(ndev, i); ++ napi_gro_receive(&priv->napi, skb); ++ ++ return 0; ++ /* If the frame need to be dropped, we simply reuse the buffer */ ++discard_frame: ++ ddesc->ctl = cpu_to_le32(DESC_BUF_MAX); ++ /* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ ++ wmb(); ++ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; ++ return 0; ++} ++ ++/* Iterate over dma_desc for finding completed xmit. ++ * ++ * The problem is: how to know that a descriptor is sent and not just in ++ * preparation. ++ * Need to have status=0 and st set but this is the state of first frame just ++ * before setting the own-by-DMA bit. ++ * The solution is to used the artificial value DCLEAN. ++ */ ++static int sun8i_emac_complete_xmit(struct net_device *ndev, int budget) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc; ++ int frame_len; ++ int work = 0; ++ unsigned int bytes_compl = 0, pkts_compl = 0; ++ u32 dstatus; ++ ++ do { ++ ddesc = priv->dd_tx + priv->tx_dirty; ++ ++ if (ddesc->status & EMAC_COULD_BE_USED_BY_DMA) ++ goto xmit_end; ++ ++ if (ddesc->status == DCLEAN) ++ goto xmit_end; ++ ++ dstatus = cpu_to_le32(ddesc->status); ++ ++ if (ddesc->status == 0 && !ddesc->ctl) { ++ dev_err(priv->dev, "BUG: reached the void %d %d\n", ++ priv->tx_dirty, priv->tx_slot); ++ goto xmit_end; ++ } ++ ++ /* TX_UNDERFLOW_ERR */ ++ if (dstatus & BIT(1)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_DEFER_ERR */ ++ if (dstatus & BIT(2)) ++ priv->ndev->stats.tx_errors++; ++ /* BIT 6:3 numbers of collisions */ ++ if (dstatus & 0x78) ++ priv->ndev->stats.collisions += ++ (dstatus & 0x78) >> 3; ++ /* TX_COL_ERR_1 */ ++ if (dstatus & BIT(8)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_COL_ERR_0 */ ++ if (dstatus & BIT(9)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_CRS_ERR */ ++ if (dstatus & BIT(10)) ++ priv->ndev->stats.tx_carrier_errors++; ++ /* TX_PAYLOAD_ERR */ ++ if (dstatus & BIT(12)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_LENGTH_ERR */ ++ if (dstatus & BIT(14)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_HEADER_ERR */ ++ if (dstatus & BIT(16)) ++ priv->ndev->stats.tx_errors++; ++ ++ frame_len = le32_to_cpu(ddesc->ctl) & 0x3FFF; ++ bytes_compl += frame_len; ++ ++ if (priv->txl[priv->tx_dirty].map == MAP_SINGLE) ++ dma_unmap_single(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ frame_len, DMA_TO_DEVICE); ++ else ++ dma_unmap_page(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ frame_len, DMA_TO_DEVICE); ++ /* we can free skb only on last frame */ ++ if (priv->txl[priv->tx_dirty].skb && ++ (ddesc->ctl & EMAC_DSC_TX_LAST)) { ++ dev_kfree_skb_irq(priv->txl[priv->tx_dirty].skb); ++ pkts_compl++; ++ } ++ ++ priv->txl[priv->tx_dirty].skb = NULL; ++ priv->txl[priv->tx_dirty].map = 0; ++ ddesc->ctl = 0; ++ /* setting status to DCLEAN is the last value to be set */ ++ wmb(); ++ ddesc->status = DCLEAN; ++ work++; ++ ++ rb_inc(&priv->tx_dirty, priv->nbdesc_tx); ++ ddesc = priv->dd_tx + priv->tx_dirty; ++ } while (ddesc->ctl && ++ !(ddesc->status & EMAC_COULD_BE_USED_BY_DMA) && ++ work < budget); ++ ++xmit_end: ++ netdev_completed_queue(ndev, pkts_compl, bytes_compl); ++ ++ /* if we don't have handled all packets */ ++ if (work < budget) ++ work = 0; ++ ++ if (netif_queue_stopped(ndev) && ++ rb_tx_numfreedesc(ndev) > MAX_SKB_FRAGS + 1) ++ netif_wake_queue(ndev); ++ return work; ++} ++ ++static int sun8i_emac_poll(struct napi_struct *napi, int budget) ++{ ++ struct sun8i_emac_priv *priv = ++ container_of(napi, struct sun8i_emac_priv, napi); ++ struct net_device *ndev = priv->ndev; ++ int worked; ++ struct dma_desc *ddesc; ++ ++ priv->estats.napi_schedule++; ++ worked = sun8i_emac_complete_xmit(ndev, budget); ++ ++ ddesc = priv->dd_rx + priv->rx_dirty; ++ while (!(ddesc->status & EMAC_COULD_BE_USED_BY_DMA) && ++ worked < budget) { ++ sun8i_emac_rx_from_ddesc(ndev, priv->rx_dirty); ++ worked++; ++ rb_inc(&priv->rx_dirty, priv->nbdesc_rx); ++ ddesc = priv->dd_rx + priv->rx_dirty; ++ }; ++ if (worked < budget) { ++ priv->estats.napi_underflow++; ++ napi_complete(&priv->napi); ++ writel(EMAC_RX_INT | EMAC_TX_INT, priv->base + EMAC_INT_EN); ++ } ++ return worked; ++} ++ ++static int sun8i_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ struct net_device *ndev = bus->priv; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int err; ++ u32 reg; ++ ++ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, ++ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ if (err) { ++ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); ++ return err; ++ } ++ ++ reg &= ~MDIO_CMD_MII_WRITE; ++ reg &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; ++ reg |= (phy_reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & ++ MDIO_CMD_MII_PHY_REG_ADDR_MASK; ++ ++ reg &= ~MDIO_CMD_MII_PHY_ADDR_MASK; ++ ++ reg |= (phy_addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & ++ MDIO_CMD_MII_PHY_ADDR_MASK; ++ ++ reg |= MDIO_CMD_MII_BUSY; ++ ++ writel(reg, priv->base + EMAC_MDIO_CMD); ++ ++ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, ++ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ ++ if (err) { ++ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); ++ return err; ++ } ++ ++ return readl(priv->base + EMAC_MDIO_DATA); ++} ++ ++static int sun8i_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, ++ u16 data) ++{ ++ struct net_device *ndev = bus->priv; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 reg; ++ int err; ++ ++ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, ++ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ if (err) { ++ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); ++ return err; ++ } ++ ++ reg &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; ++ reg |= (phy_reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & ++ MDIO_CMD_MII_PHY_REG_ADDR_MASK; ++ ++ reg &= ~MDIO_CMD_MII_PHY_ADDR_MASK; ++ reg |= (phy_addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & ++ MDIO_CMD_MII_PHY_ADDR_MASK; ++ ++ reg |= MDIO_CMD_MII_WRITE; ++ reg |= MDIO_CMD_MII_BUSY; ++ ++ writel(reg, priv->base + EMAC_MDIO_CMD); ++ writel(data, priv->base + EMAC_MDIO_DATA); ++ ++ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, ++ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ if (err) { ++ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int sun8i_emac_mdio_register(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct mii_bus *bus; ++ int ret; ++ ++ bus = mdiobus_alloc(); ++ if (!bus) { ++ netdev_err(ndev, "Failed to allocate a new mdio bus\n"); ++ return -ENOMEM; ++ } ++ ++ bus->name = dev_name(priv->dev); ++ bus->read = &sun8i_mdio_read; ++ bus->write = &sun8i_mdio_write; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%x", bus->name, priv->dev->id); ++ ++ bus->parent = priv->dev; ++ bus->priv = ndev; ++ ++ ret = of_mdiobus_register(bus, priv->mdio_node); ++ if (ret) { ++ netdev_err(ndev, "Could not register a MDIO bus: %d\n", ret); ++ mdiobus_free(bus); ++ return ret; ++ } ++ ++ priv->mdio = bus; ++ ++ return 0; ++} ++ ++static void sun8i_emac_mdio_unregister(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ mdiobus_unregister(priv->mdio); ++ mdiobus_free(priv->mdio); ++} ++ ++/* Run within phydev->lock */ ++static void sun8i_emac_adjust_link(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct phy_device *phydev = ndev->phydev; ++ int new_state = 0; ++ ++ netif_dbg(priv, link, priv->ndev, ++ "%s link=%x duplex=%x speed=%x\n", __func__, ++ phydev->link, phydev->duplex, phydev->speed); ++ if (!phydev) ++ return; ++ ++ if (phydev->link) { ++ if (phydev->duplex != priv->duplex) { ++ new_state = 1; ++ priv->duplex = phydev->duplex; ++ } ++ if (phydev->pause) ++ sun8i_emac_flow_ctrl(priv, phydev->duplex, ++ priv->flow_ctrl); ++ ++ if (phydev->speed != priv->speed) { ++ new_state = 1; ++ priv->speed = phydev->speed; ++ } ++ ++ if (priv->link == 0) { ++ new_state = 1; ++ priv->link = phydev->link; ++ } ++ ++ netif_dbg(priv, link, priv->ndev, ++ "%s new=%d link=%d pause=%d\n", ++ __func__, new_state, priv->link, phydev->pause); ++ if (new_state) ++ sun8i_emac_set_link_mode(priv); ++ } else if (priv->link != phydev->link) { ++ new_state = 1; ++ priv->link = 0; ++ priv->speed = 0; ++ priv->duplex = -1; ++ } ++ ++ if (new_state) ++ phy_print_status(phydev); ++} ++ ++/* H3 specific bits for EPHY */ ++#define H3_EPHY_ADDR_SHIFT 20 ++#define H3_EPHY_LED_POL BIT(17) /* 1: active low, 0: active high */ ++#define H3_EPHY_SHUTDOWN BIT(16) /* 1: shutdown, 0: power up */ ++#define H3_EPHY_SELECT BIT(15) /* 1: internal PHY, 0: external PHY */ ++ ++/* H3/A64 specific bits */ ++#define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */ ++ ++/* Generic system control EMAC_CLK bits */ ++#define SYSCON_ETXDC_MASK GENMASK(2, 0) ++#define SYSCON_ETXDC_SHIFT 10 ++#define SYSCON_ERXDC_MASK GENMASK(4, 0) ++#define SYSCON_ERXDC_SHIFT 5 ++/* EMAC PHY Interface Type */ ++#define SYSCON_EPIT BIT(2) /* 1: RGMII, 0: MII */ ++#define SYSCON_ETCS_MASK GENMASK(1, 0) ++#define SYSCON_ETCS_MII 0x0 ++#define SYSCON_ETCS_EXT_GMII 0x1 ++#define SYSCON_ETCS_INT_GMII 0x2 ++#define SYSCON_EMAC_REG 0x30 ++ ++static int sun8i_emac_set_syscon(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct device_node *node = priv->dev->of_node; ++ int ret; ++ u32 reg, val; ++ ++ reg = priv->variant->default_syscon_value; ++ ++ if (priv->variant->internal_phy) { ++ if (!priv->use_internal_phy) { ++ /* switch to external PHY interface */ ++ reg &= ~H3_EPHY_SELECT; ++ } else { ++ reg |= H3_EPHY_SELECT; ++ reg &= ~H3_EPHY_SHUTDOWN; ++ ++ if (of_property_read_bool(priv->phy_node, ++ "allwinner,leds-active-low")) ++ reg |= H3_EPHY_LED_POL; ++ ++ ret = of_mdio_parse_addr(priv->dev, priv->phy_node); ++ if (ret < 0) { ++ netdev_err(ndev, "Could not parse MDIO addr\n"); ++ return ret; ++ } ++ /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY ++ * address. No need to mask it again. ++ */ ++ reg |= ret << H3_EPHY_ADDR_SHIFT; ++ } ++ } ++ ++ if (!of_property_read_u32(node, "allwinner,tx-delay", &val)) { ++ if (val <= SYSCON_ETXDC_MASK) { ++ reg &= ~(SYSCON_ETXDC_MASK << SYSCON_ETXDC_SHIFT); ++ reg |= (val << SYSCON_ETXDC_SHIFT); ++ } else { ++ netdev_warn(ndev, "Invalid TX clock delay: %d\n", val); ++ } ++ } ++ ++ if (!of_property_read_u32(node, "allwinner,rx-delay", &val)) { ++ if (val <= SYSCON_ERXDC_MASK) { ++ reg &= ~(SYSCON_ERXDC_MASK << SYSCON_ERXDC_SHIFT); ++ reg |= (val << SYSCON_ERXDC_SHIFT); ++ } else { ++ netdev_warn(ndev, "Invalid RX clock delay: %d\n", val); ++ } ++ } ++ ++ /* Clear interface mode bits */ ++ reg &= ~(SYSCON_ETCS_MASK | SYSCON_EPIT); ++ if (priv->variant->support_rmii) ++ reg &= ~SYSCON_RMII_EN; ++ ++ switch (priv->phy_interface) { ++ case PHY_INTERFACE_MODE_MII: ++ /* default */ ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ reg |= SYSCON_EPIT | SYSCON_ETCS_INT_GMII; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ reg |= SYSCON_RMII_EN | SYSCON_ETCS_EXT_GMII; ++ break; ++ default: ++ netdev_err(ndev, "Unsupported interface mode: %s", ++ phy_modes(priv->phy_interface)); ++ return -EINVAL; ++ } ++ ++ regmap_write(priv->regmap, SYSCON_EMAC_REG, reg); ++ ++ return 0; ++} ++ ++static void sun8i_emac_unset_syscon(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 reg = priv->variant->default_syscon_value; ++ ++ regmap_write(priv->regmap, SYSCON_EMAC_REG, reg); ++} ++ ++/* Set Management Data Clock, must be call after device reset */ ++static void sun8i_emac_set_mdc(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ unsigned long rate; ++ u32 reg; ++ ++ rate = clk_get_rate(priv->ahb_clk); ++ if (rate > 160000000) ++ reg = 0x3 << 20; /* AHB / 128 */ ++ else if (rate > 80000000) ++ reg = 0x2 << 20; /* AHB / 64 */ ++ else if (rate > 40000000) ++ reg = 0x1 << 20; /* AHB / 32 */ ++ else ++ reg = 0x0 << 20; /* AHB / 16 */ ++ netif_dbg(priv, link, ndev, "MDC auto : %x\n", reg); ++ writel(reg, priv->base + EMAC_MDIO_CMD); ++} ++ ++/* "power" the device, by enabling clk/reset/regulators */ ++static int sun8i_emac_power(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int ret; ++ ++ ret = clk_prepare_enable(priv->ahb_clk); ++ if (ret) { ++ netdev_err(ndev, "Could not enable AHB clock\n"); ++ return ret; ++ } ++ ++ if (priv->rst_mac) { ++ ret = reset_control_deassert(priv->rst_mac); ++ if (ret) { ++ netdev_err(ndev, "Could not deassert reset\n"); ++ goto err_reset; ++ } ++ } ++ ++ if (priv->ephy_clk) { ++ ret = clk_prepare_enable(priv->ephy_clk); ++ if (ret) { ++ netdev_err(ndev, "Could not enable EPHY clock\n"); ++ goto err_ephy_clk; ++ } ++ } ++ ++ if (priv->rst_ephy) { ++ ret = reset_control_deassert(priv->rst_ephy); ++ if (ret) { ++ netdev_err(ndev, "Could not deassert EPHY reset\n"); ++ goto err_ephy_reset; ++ } ++ } ++ ++ return 0; ++ ++err_ephy_reset: ++ if (priv->ephy_clk) ++ clk_disable_unprepare(priv->ephy_clk); ++err_ephy_clk: ++ if (priv->rst_mac) ++ reset_control_assert(priv->rst_mac); ++err_reset: ++ clk_disable_unprepare(priv->ahb_clk); ++ return ret; ++} ++ ++/* "Unpower" the device, disabling clocks and regulators, asserting reset */ ++static void sun8i_emac_unpower(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ if (priv->rst_ephy) ++ reset_control_assert(priv->rst_ephy); ++ ++ if (priv->ephy_clk) ++ clk_disable_unprepare(priv->ephy_clk); ++ ++ if (priv->rst_mac) ++ reset_control_assert(priv->rst_mac); ++ ++ clk_disable_unprepare(priv->ahb_clk); ++} ++ ++static int sun8i_emac_init(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct device_node *node = priv->dev->of_node; ++ const u8 *addr; ++ ++ /* Try to get MAC address from DT, or assign a random one */ ++ addr = of_get_mac_address(node); ++ if (addr) ++ ether_addr_copy(ndev->dev_addr, addr); ++ else ++ eth_hw_addr_random(ndev); ++ ++ return 0; ++} ++ ++static int sun8i_emac_mdio_probe(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct phy_device *phydev = NULL; ++ ++ phydev = of_phy_connect(ndev, priv->phy_node, &sun8i_emac_adjust_link, ++ 0, priv->phy_interface); ++ ++ if (!phydev) { ++ netdev_err(ndev, "Could not attach to PHY\n"); ++ return -ENODEV; ++ } ++ ++ phy_attached_info(phydev); ++ ++ /* mask with MAC supported features */ ++ phydev->supported &= PHY_GBIT_FEATURES; ++ phydev->advertising = phydev->supported; ++ ++ priv->link = 0; ++ priv->speed = 0; ++ priv->duplex = -1; ++ ++ return 0; ++} ++ ++/* Allocate both RX and TX ring buffer and init them ++ * This function also write the startbase of thoses ring in the device. ++ * All structures that help managing thoses rings are also handled ++ * by this functions (rx_skb/txl) ++ */ ++static int sun8i_emac_alloc_rings(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc; ++ int err, i; ++ ++ priv->rx_skb = kcalloc(priv->nbdesc_rx, sizeof(struct sk_buff *), ++ GFP_KERNEL); ++ if (!priv->rx_skb) { ++ err = -ENOMEM; ++ goto rx_skb_error; ++ } ++ priv->txl = kcalloc(priv->nbdesc_tx, sizeof(struct txinfo), GFP_KERNEL); ++ if (!priv->txl) { ++ err = -ENOMEM; ++ goto tx_error; ++ } ++ ++ /* allocate/init RX ring */ ++ priv->dd_rx = dma_zalloc_coherent(priv->dev, ++ priv->nbdesc_rx * sizeof(struct dma_desc), ++ &priv->dd_rx_phy, GFP_KERNEL); ++ if (!priv->dd_rx) { ++ dev_err(priv->dev, "ERROR: cannot allocate DMA RX buffer"); ++ err = -ENOMEM; ++ goto dma_rx_error; ++ } ++ ddesc = priv->dd_rx; ++ for (i = 0; i < priv->nbdesc_rx; i++) { ++ sun8i_emac_rx_skb(ndev, i); ++ ddesc->next = cpu_to_le32(priv->dd_rx_phy + (i + 1) ++ * sizeof(struct dma_desc)); ++ ddesc++; ++ } ++ /* last descriptor point back to first one */ ++ ddesc--; ++ ddesc->next = cpu_to_le32(priv->dd_rx_phy); ++ ++ /* allocate/init TX ring */ ++ priv->dd_tx = dma_zalloc_coherent(priv->dev, ++ priv->nbdesc_tx * sizeof(struct dma_desc), ++ &priv->dd_tx_phy, GFP_KERNEL); ++ if (!priv->dd_tx) { ++ dev_err(priv->dev, "ERROR: cannot allocate DMA TX buffer"); ++ err = -ENOMEM; ++ goto dma_tx_error; ++ } ++ ddesc = priv->dd_tx; ++ for (i = 0; i < priv->nbdesc_tx; i++) { ++ ddesc->status = DCLEAN; ++ ddesc->ctl = 0; ++ ddesc->next = cpu_to_le32(priv->dd_tx_phy + (i + 1) ++ * sizeof(struct dma_desc)); ++ ddesc++; ++ } ++ /* last descriptor point back to first one */ ++ ddesc--; ++ ddesc->next = cpu_to_le32(priv->dd_tx_phy); ++ i--; ++ ++ priv->tx_slot = 0; ++ priv->tx_dirty = 0; ++ priv->rx_dirty = 0; ++ ++ /* write start of RX ring descriptor */ ++ writel(priv->dd_rx_phy, priv->base + EMAC_RX_DESC_LIST); ++ /* write start of TX ring descriptor */ ++ writel(priv->dd_tx_phy, priv->base + EMAC_TX_DESC_LIST); ++ ++ return 0; ++dma_tx_error: ++ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), ++ priv->dd_rx, priv->dd_rx_phy); ++dma_rx_error: ++ kfree(priv->txl); ++tx_error: ++ kfree(priv->rx_skb); ++rx_skb_error: ++ return err; ++} ++ ++static int sun8i_emac_open(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int err; ++ u32 v; ++ ++ err = sun8i_emac_power(ndev); ++ if (err) ++ return err; ++ ++ err = request_irq(priv->irq, sun8i_emac_dma_interrupt, 0, ++ dev_name(priv->dev), ndev); ++ if (err) { ++ dev_err(priv->dev, "Cannot request IRQ: %d\n", err); ++ goto err_power; ++ } ++ ++ /* Set interface mode (and configure internal PHY on H3) */ ++ err = sun8i_emac_set_syscon(ndev); ++ if (err) ++ goto err_irq; ++ ++ /* Do SOFT RST */ ++ v = readl(priv->base + EMAC_BASIC_CTL1); ++ writel(v | 0x01, priv->base + EMAC_BASIC_CTL1); ++ ++ err = readl_poll_timeout(priv->base + EMAC_BASIC_CTL1, v, ++ !(v & 0x01), 100, 10000); ++ if (err) { ++ dev_err(priv->dev, "EMAC reset timeout\n"); ++ err = -EFAULT; ++ goto err_syscon; ++ } ++ ++ sun8i_emac_set_mdc(ndev); ++ ++ err = sun8i_emac_mdio_register(ndev); ++ if (err) ++ goto err_syscon; ++ ++ err = sun8i_emac_mdio_probe(ndev); ++ if (err) ++ goto err_syscon; ++ ++ /* DMA */ ++ v = (8 << 24);/* burst len */ ++ writel(v, priv->base + EMAC_BASIC_CTL1); ++ ++ writel(EMAC_RX_INT | EMAC_TX_INT, priv->base + EMAC_INT_EN); ++ ++ v = readl(priv->base + EMAC_RX_CTL0); ++ /* CHECK_CRC */ ++ if (ndev->features & NETIF_F_RXCSUM) ++ v |= EMAC_RX_DO_CRC; ++ else ++ v &= ~EMAC_RX_DO_CRC; ++ /* STRIP_FCS */ ++ if (ndev->features & NETIF_F_RXFCS) ++ v &= ~EMAC_RX_STRIP_FCS; ++ else ++ v |= EMAC_RX_STRIP_FCS; ++ writel(v, priv->base + EMAC_RX_CTL0); ++ ++ v = readl(priv->base + EMAC_TX_CTL1); ++ /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ ++ v |= BIT(1); ++ /* Undocumented bit (called TX_NEXT_FRM in BSP), the original comment is ++ * "Operating on second frame increase the performance ++ * especially when transmit store-and-forward is used." ++ */ ++ v |= BIT(2); ++ writel(v, priv->base + EMAC_TX_CTL1); ++ ++ v = readl(priv->base + EMAC_RX_CTL1); ++ /* RX_MD RX DMA reads data from RX DMA FIFO to host memory after a ++ * complete frame has been written to RX DMA FIFO ++ */ ++ v |= BIT(1); ++ writel(v, priv->base + EMAC_RX_CTL1); ++ ++ sun8i_emac_set_macaddr(priv, ndev->dev_addr, 0); ++ ++ err = sun8i_emac_alloc_rings(ndev); ++ if (err) { ++ netdev_err(ndev, "Fail to allocate rings\n"); ++ goto err_mdio; ++ } ++ ++ phy_start(ndev->phydev); ++ ++ sun8i_emac_start_rx(ndev); ++ sun8i_emac_start_tx(ndev); ++ ++ netif_napi_add(ndev, &priv->napi, sun8i_emac_poll, 64); ++ napi_enable(&priv->napi); ++ netif_start_queue(ndev); ++ ++ return 0; ++err_mdio: ++ phy_disconnect(ndev->phydev); ++err_syscon: ++ sun8i_emac_unset_syscon(ndev); ++err_irq: ++ free_irq(priv->irq, ndev); ++err_power: ++ sun8i_emac_unpower(ndev); ++ return err; ++} ++ ++/* Clean the TX ring of any accepted skb for xmit */ ++static void sun8i_emac_tx_clean(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int i; ++ struct dma_desc *ddesc; ++ int frame_len; ++ ++ for (i = 0; i < priv->nbdesc_tx; i++) { ++ if (priv->txl[i].skb) { ++ ddesc = priv->dd_tx + i; ++ frame_len = le32_to_cpu(ddesc->ctl) & 0x3FFF; ++ switch (priv->txl[i].map) { ++ case MAP_SINGLE: ++ dma_unmap_single(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ frame_len, DMA_TO_DEVICE); ++ break; ++ case MAP_PAGE: ++ dma_unmap_page(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ frame_len, DMA_TO_DEVICE); ++ break; ++ default: ++ dev_err(priv->dev, "Trying to free an empty slot\n"); ++ continue; ++ } ++ dev_kfree_skb_any(priv->txl[i].skb); ++ priv->txl[i].skb = NULL; ++ ddesc->ctl = 0; ++ ddesc->status = DCLEAN; ++ } ++ } ++ priv->tx_slot = 0; ++ priv->tx_dirty = 0; ++} ++ ++/* Clean the RX ring */ ++static void sun8i_emac_rx_clean(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int i; ++ struct dma_desc *ddesc; ++ ++ /* clean RX ring */ ++ for (i = 0; i < priv->nbdesc_rx; i++) ++ if (priv->rx_skb[i]) { ++ ddesc = priv->dd_rx + i; ++ dma_unmap_single(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ DESC_BUF_MAX, DMA_FROM_DEVICE); ++ dev_kfree_skb_any(priv->rx_skb[i]); ++ priv->rx_skb[i] = NULL; ++ } ++} ++ ++static int sun8i_emac_stop(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ napi_disable(&priv->napi); ++ ++ sun8i_emac_stop_tx(ndev); ++ sun8i_emac_stop_rx(ndev); ++ ++ phy_stop(ndev->phydev); ++ phy_disconnect(ndev->phydev); ++ ++ sun8i_emac_mdio_unregister(ndev); ++ ++ sun8i_emac_unset_syscon(ndev); ++ ++ free_irq(priv->irq, ndev); ++ ++ sun8i_emac_rx_clean(ndev); ++ sun8i_emac_tx_clean(ndev); ++ ++ kfree(priv->rx_skb); ++ kfree(priv->txl); ++ ++ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), ++ priv->dd_rx, priv->dd_rx_phy); ++ dma_free_coherent(priv->dev, priv->nbdesc_tx * sizeof(struct dma_desc), ++ priv->dd_tx, priv->dd_tx_phy); ++ ++ sun8i_emac_unpower(ndev); ++ ++ return 0; ++} ++ ++static netdev_tx_t sun8i_emac_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc; ++ struct dma_desc *first; ++ int i = 0, rbd_first; ++ unsigned int len, fraglen, tlen; ++ u32 v; ++ int n; ++ int nf; ++ const skb_frag_t *frag; ++ int do_csum = 0; ++ ++ if (skb_put_padto(skb, ETH_ZLEN)) ++ return NETDEV_TX_OK; ++ len = skb_headlen(skb); ++ ++ n = skb_shinfo(skb)->nr_frags; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ do_csum = 1; ++ priv->estats.tx_hw_csum++; ++ } ++ netif_dbg(priv, tx_queued, ndev, "%s len=%u skblen=%u %x\n", __func__, ++ len, skb->len, ++ (skb->ip_summed == CHECKSUM_PARTIAL)); ++ ++ /* check for contigous space ++ * We need at least 1(skb->data) + n(numfrags) + 1(one clean slot) ++ */ ++ if (rb_tx_numfreedesc(ndev) < n + 2) { ++ dev_err_ratelimited(priv->dev, "BUG!: TX is full %d %d\n", ++ priv->tx_dirty, priv->tx_slot); ++ netif_stop_queue(ndev); ++ return NETDEV_TX_BUSY; ++ } ++ i = priv->tx_slot; ++ ++ ddesc = priv->dd_tx + i; ++ first = priv->dd_tx + i; ++ rbd_first = i; ++ ++ ddesc->buf_addr = dma_map_single(priv->dev, skb->data, len, ++ DMA_TO_DEVICE); ++ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { ++ dev_err(priv->dev, "ERROR: Cannot map buffer for DMA\n"); ++ goto xmit_error; ++ } ++ /* We cannot direcly use cpu_to_le32() after dma_map_single ++ * since dma_mapping_error use it ++ */ ++ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); ++ priv->txl[i].map = MAP_SINGLE; ++ priv->txl[i].skb = skb; ++ ++ tlen = len; ++ ddesc->ctl = le32_to_cpu(len); ++ /* Undocumented bit that make it works ++ * Without it, packets never be sent on H3 SoC ++ */ ++ ddesc->ctl |= EMAC_MAGIC_TX_BIT; ++ if (do_csum) ++ ddesc->ctl |= EMAC_TX_DO_CRC; ++ ++ /* handle fragmented skb, one descriptor per fragment */ ++ for (nf = 0; nf < n; nf++) { ++ frag = &skb_shinfo(skb)->frags[nf]; ++ rb_inc(&i, priv->nbdesc_tx); ++ priv->txl[i].skb = skb; ++ ddesc = priv->dd_tx + i; ++ fraglen = skb_frag_size(frag); ++ ddesc->ctl = le32_to_cpu(fraglen); ++ tlen += fraglen, ++ ddesc->ctl |= EMAC_MAGIC_TX_BIT; ++ if (do_csum) ++ ddesc->ctl |= EMAC_TX_DO_CRC; ++ ++ ddesc->buf_addr = skb_frag_dma_map(priv->dev, frag, 0, ++ fraglen, DMA_TO_DEVICE); ++ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { ++ dev_err(priv->dev, "Cannot map buffer for DMA\n"); ++ goto xmit_error; ++ } ++ /* Cannot directly use cpu_to_le32() after skb_frag_dma_map ++ * since dma_mapping_error use it ++ */ ++ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); ++ priv->txl[i].map = MAP_PAGE; ++ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; ++ } ++ ++ /* frame end */ ++ ddesc->ctl |= EMAC_DSC_TX_LAST; ++ /* We want an interrupt after transmission */ ++ ddesc->ctl |= EMAC_WANT_INT; ++ ++ rb_inc(&i, priv->nbdesc_tx); ++ ++ /* This line was previously after DMA start, but with that we hit a ++ * small race with complete_xmit() where we complete more data than ++ * sent. ++ * The packet is sent just after EMAC_COULD_BE_USED_BY_DMA flag set and ++ * complete_xmit fire just after before netdev_sent_queue(). ++ * This race could be observed only when overflowing a gigabit line. ++ */ ++ netdev_sent_queue(ndev, skb->len); ++ ++ /* frame begin */ ++ first->ctl |= EMAC_DSC_TX_FIRST; ++ wmb();/* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ ++ first->status = EMAC_COULD_BE_USED_BY_DMA; ++ priv->tx_slot = i; ++ ++ /* Trying to optimize this (recording DMA start/stop) seems ++ * to lead to errors. So we always start DMA. ++ */ ++ v = readl(priv->base + EMAC_TX_CTL1); ++ v |= EMAC_TX_DMA_START; ++ v |= EMAC_TX_DMA_EN; ++ writel_relaxed(v, priv->base + EMAC_TX_CTL1); ++ ++ if (rb_tx_numfreedesc(ndev) < MAX_SKB_FRAGS + 1) { ++ netif_stop_queue(ndev); ++ priv->estats.tx_stop_queue++; ++ } ++ priv->estats.tx_used_desc = rb_tx_numfreedesc(ndev); ++ priv->ndev->stats.tx_packets++; ++ priv->ndev->stats.tx_bytes += tlen; ++ ++ return NETDEV_TX_OK; ++ ++xmit_error: ++ /* destroy skb and return TX OK Documentation/DMA-API-HOWTO.txt */ ++ /* clean descritors from rbd_first to i */ ++ ddesc->ctl = 0; ++ /* setting status to DCLEAN is the last value to be set */ ++ wmb(); ++ ddesc->status = DCLEAN; ++ do { ++ ddesc = priv->dd_tx + rbd_first; ++ ddesc->ctl = 0; ++ /* setting status to DCLEAN is the last value to be set */ ++ wmb(); ++ ddesc->status = DCLEAN; ++ rb_inc(&rbd_first, priv->nbdesc_tx); ++ } while (rbd_first != i); ++ dev_kfree_skb_any(skb); ++ return NETDEV_TX_OK; ++} ++ ++static int sun8i_emac_change_mtu(struct net_device *ndev, int new_mtu) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int max_mtu; ++ ++ dev_info(priv->dev, "%s set MTU to %d\n", __func__, new_mtu); ++ ++ if (netif_running(ndev)) { ++ dev_err(priv->dev, "%s: must be stopped to change its MTU\n", ++ ndev->name); ++ return -EBUSY; ++ } ++ ++ max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN); ++ ++ if ((new_mtu < 68) || (new_mtu > max_mtu)) { ++ dev_err(priv->dev, "%s: invalid MTU, max MTU is: %d\n", ++ ndev->name, max_mtu); ++ return -EINVAL; ++ } ++ ++ ndev->mtu = new_mtu; ++ netdev_update_features(ndev); ++ return 0; ++} ++ ++static int sun8i_emac_set_features(struct net_device *ndev, ++ netdev_features_t features) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ v = readl(priv->base + EMAC_BASIC_CTL0); ++ if (features & NETIF_F_LOOPBACK && netif_running(ndev)) { ++ netif_info(priv, hw, ndev, "Set loopback features"); ++ v |= BIT(1); ++ } else { ++ netif_info(priv, hw, ndev, "Unset loopback features"); ++ v &= ~BIT(1); ++ } ++ writel(v, priv->base + EMAC_BASIC_CTL0); ++ ++ v = readl(priv->base + EMAC_RX_CTL0); ++ if (features & NETIF_F_RXCSUM) { ++ v |= EMAC_RX_DO_CRC; ++ netif_info(priv, hw, ndev, "Doing RX CRC check by hardware"); ++ } else { ++ v &= ~EMAC_RX_DO_CRC; ++ netif_info(priv, hw, ndev, "No RX CRC check by hardware"); ++ } ++ if (features & NETIF_F_RXFCS) { ++ v &= ~EMAC_RX_STRIP_FCS; ++ netif_info(priv, hw, ndev, "Keep FCS"); ++ } else { ++ v |= EMAC_RX_STRIP_FCS; ++ netif_info(priv, hw, ndev, "Strip FCS"); ++ } ++ writel(v, priv->base + EMAC_RX_CTL0); ++ ++ netif_dbg(priv, drv, ndev, "%s %llx %x\n", __func__, features, v); ++ ++ return 0; ++} ++ ++static void sun8i_emac_set_rx_mode(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v = 0; ++ int i = 0; ++ struct netdev_hw_addr *ha; ++ ++ /* Receive all multicast frames */ ++ v |= BIT(16); ++ /* Receive all control frames */ ++ v |= BIT(13); ++ if (ndev->flags & IFF_PROMISC) ++ v |= BIT(1); ++ if (netdev_uc_count(ndev) > 7) { ++ v |= BIT(1); ++ } else { ++ netdev_for_each_uc_addr(ha, ndev) { ++ i++; ++ sun8i_emac_set_macaddr(priv, ha->addr, i); ++ } ++ } ++ writel(v, priv->base + EMAC_RX_FRM_FLT); ++} ++ ++static void sun8i_emac_tx_timeout(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ netdev_err(ndev, "%s\n", __func__); ++ ++ sun8i_emac_stop_tx(ndev); ++ ++ sun8i_emac_tx_clean(ndev); ++ ++ /* write start of the new TX ring descriptor */ ++ writel(priv->dd_tx_phy, priv->base + EMAC_TX_DESC_LIST); ++ ++ sun8i_emac_start_tx(ndev); ++ ++ netdev_reset_queue(ndev); ++ ++ priv->estats.tx_timeout++; ++ ndev->stats.tx_errors++; ++ netif_wake_queue(ndev); ++} ++ ++static int sun8i_emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) ++{ ++ struct phy_device *phydev = ndev->phydev; ++ ++ if (!netif_running(ndev)) ++ return -EINVAL; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_mii_ioctl(phydev, rq, cmd); ++} ++ ++static int sun8i_emac_check_if_running(struct net_device *ndev) ++{ ++ if (!netif_running(ndev)) ++ return -EINVAL; ++ return 0; ++} ++ ++static int sun8i_emac_get_sset_count(struct net_device *ndev, int sset) ++{ ++ switch (sset) { ++ case ETH_SS_STATS: ++ return ARRAY_SIZE(estats_str); ++ } ++ return -EOPNOTSUPP; ++} ++ ++static int sun8i_emac_ethtool_get_settings(struct net_device *ndev, ++ struct ethtool_cmd *cmd) ++{ ++ struct phy_device *phy = ndev->phydev; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ if (!phy) { ++ netdev_err(ndev, "%s: %s: PHY is not registered\n", ++ __func__, ndev->name); ++ return -ENODEV; ++ } ++ ++ if (!netif_running(ndev)) { ++ dev_err(priv->dev, "interface disabled: we cannot track link speed / duplex setting\n"); ++ return -EBUSY; ++ } ++ ++ return phy_ethtool_gset(phy, cmd); ++} ++ ++static int sun8i_emac_ethtool_set_settings(struct net_device *ndev, ++ struct ethtool_cmd *cmd) ++{ ++ struct phy_device *phy = ndev->phydev; ++ ++ return phy_ethtool_sset(phy, cmd); ++} ++ ++static void sun8i_emac_ethtool_getdrvinfo(struct net_device *ndev, ++ struct ethtool_drvinfo *info) ++{ ++ strlcpy(info->driver, "sun8i_emac", sizeof(info->driver)); ++ strcpy(info->version, "00"); ++ info->fw_version[0] = '\0'; ++} ++ ++static void sun8i_emac_ethtool_stats(struct net_device *ndev, ++ struct ethtool_stats *dummy, u64 *data) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ memcpy(data, &priv->estats, ++ sun8i_emac_get_sset_count(ndev, ETH_SS_STATS) * sizeof(u64)); ++} ++ ++static void sun8i_emac_ethtool_strings(struct net_device *dev, u32 stringset, ++ u8 *buffer) ++{ ++ switch (stringset) { ++ case ETH_SS_STATS: ++ memcpy(buffer, &estats_str, sizeof(estats_str)); ++ break; ++ } ++} ++ ++static u32 sun8i_emac_ethtool_getmsglevel(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ return priv->msg_enable; ++} ++ ++static void sun8i_emac_ethtool_setmsglevel(struct net_device *ndev, u32 level) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ priv->msg_enable = level; ++} ++ ++static void sun8i_emac_get_pauseparam(struct net_device *ndev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ pause->rx_pause = 0; ++ pause->tx_pause = 0; ++ pause->autoneg = ndev->phydev->autoneg; ++ ++ if (priv->flow_ctrl & EMAC_FLOW_RX) ++ pause->rx_pause = 1; ++ if (priv->flow_ctrl & EMAC_FLOW_TX) ++ pause->tx_pause = 1; ++} ++ ++static int sun8i_emac_set_pauseparam(struct net_device *ndev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct phy_device *phy = ndev->phydev; ++ int new_pause = 0; ++ int ret = 0; ++ ++ if (pause->rx_pause) ++ new_pause |= EMAC_FLOW_RX; ++ if (pause->tx_pause) ++ new_pause |= EMAC_FLOW_TX; ++ ++ priv->flow_ctrl = new_pause; ++ phy->autoneg = pause->autoneg; ++ ++ if (phy->autoneg) { ++ if (netif_running(ndev)) ++ ret = phy_start_aneg(phy); ++ } else { ++ sun8i_emac_flow_ctrl(priv, phy->duplex, priv->flow_ctrl); ++ } ++ return ret; ++} ++ ++static void sun8i_emac_ethtool_get_ringparam(struct net_device *ndev, ++ struct ethtool_ringparam *ring) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ ring->rx_pending = priv->nbdesc_rx; ++ ring->tx_pending = priv->nbdesc_tx; ++} ++ ++static int sun8i_emac_ethtool_set_ringparam(struct net_device *ndev, ++ struct ethtool_ringparam *ring) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int err; ++ ++ if (ring->rx_max_pending || ring->rx_mini_max_pending || ++ ring->rx_jumbo_max_pending || ring->rx_mini_pending || ++ ring->rx_jumbo_pending || ring->tx_max_pending) ++ return -EINVAL; ++ ++ if (ring->tx_pending < MAX_SKB_FRAGS + 1) { ++ netdev_err(ndev, "The number of TX descriptors is too low"); ++ return -EINVAL; ++ } ++ ++ sun8i_emac_stop_tx(ndev); ++ sun8i_emac_stop_rx(ndev); ++ ++ sun8i_emac_rx_clean(ndev); ++ sun8i_emac_tx_clean(ndev); ++ ++ kfree(priv->rx_skb); ++ kfree(priv->txl); ++ ++ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), ++ priv->dd_rx, priv->dd_rx_phy); ++ dma_free_coherent(priv->dev, priv->nbdesc_tx * sizeof(struct dma_desc), ++ priv->dd_tx, priv->dd_tx_phy); ++ ++ priv->nbdesc_rx = ring->rx_pending; ++ priv->nbdesc_tx = ring->tx_pending; ++ err = sun8i_emac_alloc_rings(ndev); ++ if (err) { ++ /* Fatal error, we cannot re start */ ++ netdev_err(ndev, "Fail to allocate rings\n"); ++ return -EFAULT; ++ } ++ ++ sun8i_emac_start_rx(ndev); ++ sun8i_emac_start_tx(ndev); ++ ++ netif_start_queue(ndev); ++ ++ netdev_info(ndev, "Ring Param settings: rx: %d, tx %d\n", ++ ring->rx_pending, ring->tx_pending); ++ return 0; ++} ++ ++static const struct ethtool_ops sun8i_emac_ethtool_ops = { ++ .begin = sun8i_emac_check_if_running, ++ .get_settings = sun8i_emac_ethtool_get_settings, ++ .set_settings = sun8i_emac_ethtool_set_settings, ++ .get_link = ethtool_op_get_link, ++ .get_pauseparam = sun8i_emac_get_pauseparam, ++ .set_pauseparam = sun8i_emac_set_pauseparam, ++ .get_ethtool_stats = sun8i_emac_ethtool_stats, ++ .get_strings = sun8i_emac_ethtool_strings, ++ .get_sset_count = sun8i_emac_get_sset_count, ++ .get_drvinfo = sun8i_emac_ethtool_getdrvinfo, ++ .get_msglevel = sun8i_emac_ethtool_getmsglevel, ++ .set_msglevel = sun8i_emac_ethtool_setmsglevel, ++ .get_ringparam = sun8i_emac_ethtool_get_ringparam, ++ .set_ringparam = sun8i_emac_ethtool_set_ringparam, ++}; ++ ++static const struct net_device_ops sun8i_emac_netdev_ops = { ++ .ndo_init = sun8i_emac_init, ++ .ndo_open = sun8i_emac_open, ++ .ndo_start_xmit = sun8i_emac_xmit, ++ .ndo_stop = sun8i_emac_stop, ++ .ndo_change_mtu = sun8i_emac_change_mtu, ++ .ndo_set_features = sun8i_emac_set_features, ++ .ndo_set_rx_mode = sun8i_emac_set_rx_mode, ++ .ndo_tx_timeout = sun8i_emac_tx_timeout, ++ .ndo_do_ioctl = sun8i_emac_ioctl, ++ .ndo_set_mac_address = eth_mac_addr, ++}; ++ ++/* No locking in this function since it is guaranteed to be run once and ++ * do actions only done here: ++ * - Scheduling NAPI ++ * - Stopping interrupts ++ * - Updating stats ++ * ++ * Even when not enabled via EMAC_INT_EN, some interrupt could fire, so we need ++ * to handle all of them. ++ * Interrupts know to fire when not enabled are: ++ * - EMAC_TX_DMA_STOP_INT ++ * - EMAC_TX_BUF_UA_INT ++ * - EMAC_TX_EARLY_INT ++ * - EMAC_RX_BUF_UA_INT ++ * - EMAC_RX_EARLY_INT ++ */ ++static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *ndev = dev_id; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v, u; ++ ++ v = readl(priv->base + EMAC_INT_STA); ++ ++ /* When this bit is asserted, a frame transmission is completed. */ ++ if (v & EMAC_TX_INT) { ++ priv->estats.tx_int++; ++ writel(0, priv->base + EMAC_INT_EN); ++ napi_schedule(&priv->napi); ++ } ++ ++ /* When this bit is asserted, the TX DMA FSM is stopped. ++ * For the moment only a call to tx_timeout cause this interrupt ++ * to fire. ++ */ ++ if (v & EMAC_TX_DMA_STOP_INT) ++ priv->estats.tx_dma_stop++; ++ ++ /* When this asserted, the TX DMA can not acquire next TX descriptor ++ * and TX DMA FSM is suspended. ++ */ ++ if (v & EMAC_TX_BUF_UA_INT) { ++ priv->estats.tx_dma_ua++; ++ writel(0, priv->base + EMAC_INT_EN); ++ napi_schedule(&priv->napi); ++ } ++ ++ if (v & EMAC_TX_TIMEOUT_INT) ++ priv->estats.tx_timeout_int++; ++ ++ if (v & EMAC_TX_UNDERFLOW_INT) ++ priv->estats.tx_underflow_int++; ++ ++ /* When this bit asserted , the frame is transmitted to FIFO totally. */ ++ if (v & EMAC_TX_EARLY_INT) ++ priv->estats.tx_early_int++; ++ ++ /* When this bit is asserted, a frame reception is completed */ ++ if (v & EMAC_RX_INT) { ++ priv->estats.rx_int++; ++ writel(0, priv->base + EMAC_INT_EN); ++ napi_schedule(&priv->napi); ++ } ++ ++ /* When this asserted, the RX DMA can not acquire next RX descriptor ++ * and RX DMA FSM is suspended. ++ */ ++ if (v & EMAC_RX_BUF_UA_INT) { ++ u = readl(priv->base + EMAC_RX_CTL1); ++ writel(u | EMAC_RX_DMA_START, priv->base + EMAC_RX_CTL1); ++ priv->estats.rx_dma_ua++; ++ } ++ ++ /* Same as TX DMA STOP, but never hit it */ ++ if (v & EMAC_RX_DMA_STOP_INT) ++ priv->estats.rx_dma_stop++; ++ ++ if (v & EMAC_RX_TIMEOUT_INT) ++ priv->estats.rx_timeout_int++; ++ ++ if (v & EMAC_RX_OVERFLOW_INT) ++ priv->estats.rx_overflow_int++; ++ ++ if (v & EMAC_RX_EARLY_INT) ++ priv->estats.rx_early_int++; ++ ++ if (v & EMAC_RGMII_STA_INT) ++ priv->estats.rgmii_state_int++; ++ ++ /* the datasheet state those register as read-only ++ * but nothing work(freeze) without writing to it ++ */ ++ writel(v, priv->base + EMAC_INT_STA); ++ ++ return IRQ_HANDLED; ++} ++ ++static int sun8i_emac_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct sun8i_emac_priv *priv; ++ struct net_device *ndev; ++ struct resource *res; ++ int ret; ++ ++ ndev = alloc_etherdev(sizeof(*priv)); ++ if (!ndev) ++ return -ENOMEM; ++ ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ priv = netdev_priv(ndev); ++ platform_set_drvdata(pdev, ndev); ++ ++ priv->variant = of_device_get_match_data(&pdev->dev); ++ if (!priv->variant) { ++ dev_err(&pdev->dev, "Missing sun8i-emac variant\n"); ++ return -EINVAL; ++ } ++ ++ priv->mdio_node = of_get_child_by_name(node, "mdio"); ++ if (!priv->mdio_node) { ++ netdev_err(ndev, "Could not find a MDIO node\n"); ++ return -EINVAL; ++ } ++ ++ priv->phy_node = of_parse_phandle(node, "phy-handle", 0); ++ if (!priv->phy_node) { ++ netdev_err(ndev, "No associated PHY\n"); ++ return -EINVAL; ++ } ++ ++ priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ "syscon"); ++ if (IS_ERR(priv->regmap)) { ++ ret = PTR_ERR(priv->regmap); ++ dev_err(&pdev->dev, "unable to map SYSCON:%d\n", ret); ++ return ret; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) { ++ ret = PTR_ERR(priv->base); ++ dev_err(&pdev->dev, "Cannot request MMIO: %d\n", ret); ++ return ret; ++ } ++ ++ priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(priv->ahb_clk)) { ++ ret = PTR_ERR(priv->ahb_clk); ++ dev_err(&pdev->dev, "Cannot get AHB clock err=%d\n", ret); ++ goto probe_err; ++ } ++ ++ priv->rst_mac = devm_reset_control_get_optional(&pdev->dev, "ahb"); ++ if (IS_ERR(priv->rst_mac)) { ++ ret = PTR_ERR(priv->rst_mac); ++ if (ret == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_info(&pdev->dev, "No MAC reset control found %d\n", ret); ++ priv->rst_mac = NULL; ++ } ++ ++ priv->phy_interface = of_get_phy_mode(node); ++ if (priv->phy_interface < 0) { ++ netdev_err(ndev, "PHY interface mode node unspecified\n"); ++ return priv->phy_interface; ++ } ++ ++ switch (priv->phy_interface) { ++ case PHY_INTERFACE_MODE_MII: ++ if (!priv->variant->support_mii) ++ return -EINVAL; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ if (!priv->variant->support_rmii) ++ return -EINVAL; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ if (!priv->variant->support_rgmii) ++ return -EINVAL; ++ break; ++ default: ++ netdev_err(ndev, "Unsupported interface mode: %s", ++ phy_modes(priv->phy_interface)); ++ return -EINVAL; ++ } ++ ++ if (priv->phy_interface == priv->variant->internal_phy) ++ priv->use_internal_phy = true; ++ ++ if (priv->use_internal_phy) { ++ priv->ephy_clk = of_clk_get(priv->phy_node, 0); ++ if (IS_ERR(priv->ephy_clk)) { ++ ret = PTR_ERR(priv->ephy_clk); ++ dev_err(&pdev->dev, "Cannot get EPHY clock err=%d\n", ++ ret); ++ goto probe_err; ++ } ++ ++ priv->rst_ephy = of_reset_control_get(priv->phy_node, 0); ++ if (IS_ERR(priv->rst_ephy)) { ++ ret = PTR_ERR(priv->rst_ephy); ++ if (ret == -EPROBE_DEFER) ++ goto probe_err; ++ dev_info(&pdev->dev, ++ "No EPHY reset control found %d\n", ret); ++ priv->rst_ephy = NULL; ++ } ++ } ++ ++ priv->irq = platform_get_irq(pdev, 0); ++ if (priv->irq < 0) { ++ ret = priv->irq; ++ dev_err(&pdev->dev, "Cannot claim IRQ: %d\n", ret); ++ goto probe_err; ++ } ++ ++ spin_lock_init(&priv->tx_lock); ++ ++ ndev->netdev_ops = &sun8i_emac_netdev_ops; ++ ndev->ethtool_ops = &sun8i_emac_ethtool_ops; ++ ++ priv->ndev = ndev; ++ priv->dev = &pdev->dev; ++ ++ ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA; ++ ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_RXCSUM; ++ ndev->features |= ndev->hw_features; ++ ndev->hw_features |= NETIF_F_RXFCS; ++ ndev->hw_features |= NETIF_F_RXALL; ++ ndev->hw_features |= NETIF_F_LOOPBACK; ++ ndev->priv_flags |= IFF_UNICAST_FLT; ++ ++ ndev->watchdog_timeo = msecs_to_jiffies(5000); ++ netif_carrier_off(ndev); ++ ++ /* Benched on OPIPC with 100M, setting more than 256 does not give any ++ * perf boost ++ */ ++ priv->nbdesc_rx = 128; ++ priv->nbdesc_tx = 256; ++ ++ ret = register_netdev(ndev); ++ if (ret) { ++ dev_err(&pdev->dev, "ERROR: Register %s failed\n", ndev->name); ++ goto probe_err; ++ } ++ ++ return 0; ++ ++probe_err: ++ free_netdev(ndev); ++ return ret; ++} ++ ++static int sun8i_emac_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ ++ unregister_netdev(ndev); ++ platform_set_drvdata(pdev, NULL); ++ free_netdev(ndev); ++ ++ return 0; ++} ++ ++static const struct of_device_id sun8i_emac_of_match_table[] = { ++ { .compatible = "allwinner,sun8i-a83t-emac", ++ .data = &emac_variant_a83t }, ++ { .compatible = "allwinner,sun8i-h3-emac", ++ .data = &emac_variant_h3 }, ++ { .compatible = "allwinner,sun50i-a64-emac", ++ .data = &emac_variant_a64 }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sun8i_emac_of_match_table); ++ ++static struct platform_driver sun8i_emac_driver = { ++ .probe = sun8i_emac_probe, ++ .remove = sun8i_emac_remove, ++ .driver = { ++ .name = "sun8i-emac", ++ .of_match_table = sun8i_emac_of_match_table, ++ }, ++}; ++ ++module_platform_driver(sun8i_emac_driver); ++ ++MODULE_DESCRIPTION("sun8i Ethernet driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Corentin Labbe +Date: Fri, 7 Oct 2016 10:25:49 +0200 +Subject: [PATCH 2/9] MAINTAINERS: Add myself as maintainer of sun8i-emac + +This patch add myself as maintainer of the sun8i-emac driver. + +Signed-off-by: Corentin Labbe +--- + MAINTAINERS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index f593300..94dac86 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -582,6 +582,12 @@ S: Maintained + F: Documentation/i2c/busses/i2c-ali1563 + F: drivers/i2c/busses/i2c-ali1563.c + ++ALLWINNER SUN8I-EMAC ETHERNET DRIVER ++M: Corentin Labbe ++L: netdev@vger.kernel.org ++S: Maintained ++F: drivers/net/ethernet/allwinner/sun8i-emac.c ++ + ALLWINNER SECURITY SYSTEM + M: Corentin Labbe + L: linux-crypto@vger.kernel.org +-- +2.9.3 + +From 0c21240a091e9553751845f2aa3be0b5a88d9ac7 Mon Sep 17 00:00:00 2001 +From: Corentin LABBE +Date: Fri, 7 Oct 2016 10:25:50 +0200 +Subject: [PATCH 3/9] ARM: sun8i: dt: Add DT bindings documentation for + Allwinner sun8i-emac + +This patch adds documentation for Device-Tree bindings for the +Allwinner sun8i-emac driver. + +Signed-off-by: Corentin Labbe +--- + .../bindings/net/allwinner,sun8i-emac.txt | 70 ++++++++++++++++++++++ + 1 file changed, 70 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt + +diff --git a/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt b/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt +new file mode 100644 +index 0000000..92e4ef3b +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt +@@ -0,0 +1,70 @@ ++* Allwinner sun8i EMAC ethernet controller ++ ++Required properties: ++- compatible: should be one of the following string: ++ "allwinner,sun8i-a83t-emac" ++ "allwinner,sun8i-h3-emac" ++ "allwinner,sun50i-a64-emac" ++- reg: address and length of the register for the device. ++- syscon: A phandle to the syscon of the SoC ++- interrupts: interrupt for the device ++- clocks: A phandle to the reference clock for this device ++- clock-names: should be "ahb" ++- resets: A phandle to the reset control for this device ++- reset-names: should be "ahb" ++- phy-mode: See ethernet.txt ++- phy-handle: See ethernet.txt ++- #address-cells: shall be 1 ++- #size-cells: shall be 0 ++ ++Optional properties: ++- allwinner,tx-delay: TX clock delay chain value. Range value is 0-0x07. Default is 0) ++- allwinner,rx-delay: RX clock delay chain value. Range value is 0-0x1F. Default is 0) ++Both delay properties does not have units, there are arbitrary value. ++The TX/RX clock delay chain settings are board specific and could be found ++in vendor FEX files. ++ ++Optional properties for "allwinner,sun8i-h3-emac": ++- allwinner,leds-active-low: EPHY LEDs are active low ++ ++Required child node of emac: ++- mdio bus node: should be named mdio ++ ++Required properties of the mdio node: ++- #address-cells: shall be 1 ++- #size-cells: shall be 0 ++ ++The device node referenced by "phy" or "phy-handle" should be a child node ++of the mdio node. See phy.txt for the generic PHY bindings. ++ ++Required properties of the phy node with "allwinner,sun8i-h3-emac": ++- clocks: an extra phandle to the reference clock for the EPHY ++- resets: an extra phandle to the reset control for the EPHY ++ ++Example: ++ ++emac: ethernet@01c0b000 { ++ compatible = "allwinner,sun8i-h3-emac"; ++ syscon = <&syscon>; ++ reg = <0x01c0b000 0x104>; ++ interrupts = ; ++ resets = <&ccu RST_BUS_EMAC>; ++ reset-names = "ahb"; ++ clocks = <&ccu CLK_BUS_EMAC>; ++ clock-names = "ahb"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ phy = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ mdio: mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ int_mii_phy: ethernet-phy@1 { ++ reg = <1>; ++ clocks = <&ccu CLK_BUS_EPHY>; ++ resets = <&ccu RST_BUS_EPHY>; ++ }; ++ }; ++}; +-- +2.9.3 + +From 95d456fcdd33eb7c1512956050faba648550e06f Mon Sep 17 00:00:00 2001 +From: Corentin LABBE +Date: Fri, 7 Oct 2016 10:25:51 +0200 +Subject: [PATCH 4/9] ARM: dts: sun8i-h3: Add dt node for the syscon control + module + +This patch add the dt node for the syscon register present on the +Allwinner H3. + +Only two register are present in this syscon and the only one useful is +the one dedicated to EMAC clock. + +Signed-off-by: Corentin Labbe +--- + arch/arm/boot/dts/sun8i-h3.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi +index fdf9fdb..d658a596 100644 +--- a/arch/arm/boot/dts/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/sun8i-h3.dtsi +@@ -140,6 +140,11 @@ + #size-cells = <1>; + ranges; + ++ syscon: syscon@01c00000 { ++ compatible = "syscon"; ++ reg = <0x01c00000 0x1000>; ++ }; ++ + dma: dma-controller@01c02000 { + compatible = "allwinner,sun8i-h3-dma"; + reg = <0x01c02000 0x1000>; +-- +2.9.3 + +From 7837b66f2bce01da31ec4c005023c729ba3f17a7 Mon Sep 17 00:00:00 2001 +From: Corentin LABBE +Date: Fri, 7 Oct 2016 10:25:52 +0200 +Subject: [PATCH 5/9] ARM: dts: sun8i-h3: add sun8i-emac ethernet driver + +The sun8i-emac is an ethernet MAC hardware that support 10/100/1000 +speed. + +This patch enable the sun8i-emac on the Allwinner H3 SoC Device-tree. +The SoC H3 have an internal PHY, so optionals syscon and ephy are set. + +Signed-off-by: Corentin Labbe +--- + arch/arm/boot/dts/sun8i-h3.dtsi | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi +index d658a596..54174cf 100644 +--- a/arch/arm/boot/dts/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/sun8i-h3.dtsi +@@ -446,6 +446,30 @@ + status = "disabled"; + }; + ++ emac: ethernet@1c30000 { ++ compatible = "allwinner,sun8i-h3-emac"; ++ syscon = <&syscon>; ++ reg = <0x01c30000 0x104>; ++ interrupts = ; ++ resets = <&ccu RST_BUS_EMAC>; ++ reset-names = "ahb"; ++ clocks = <&ccu CLK_BUS_EMAC>; ++ clock-names = "ahb"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ ++ mdio: mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ int_mii_phy: ethernet-phy@1 { ++ reg = <1>; ++ clocks = <&ccu CLK_BUS_EPHY>; ++ resets = <&ccu RST_BUS_EPHY>; ++ }; ++ }; ++ }; ++ + gic: interrupt-controller@01c81000 { + compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic"; + reg = <0x01c81000 0x1000>, +-- +2.9.3 + +From e9687b752a1551ae420a94ff44254831ef464557 Mon Sep 17 00:00:00 2001 +From: Corentin LABBE +Date: Fri, 7 Oct 2016 10:25:53 +0200 +Subject: [PATCH 6/9] ARM: dts: sun8i: Enable sun8i-emac on the Orange PI PC + +The sun8i-emac hardware is present on the Orange PI PC. +It uses the internal PHY. + +This patch create the needed emac node. + +Signed-off-by: Corentin Labbe +--- + arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +index daf50b9a6..71717cc 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +@@ -54,6 +54,7 @@ + + aliases { + serial0 = &uart0; ++ ethernet0 = &emac; + }; + + chosen { +@@ -165,3 +166,10 @@ + /* USB VBUS is always on */ + status = "okay"; + }; ++ ++&emac { ++ phy-handle = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ status = "okay"; ++}; +-- +2.9.3 + +From 7df9f84ca65a398b1bb692344fa26c67235f19d3 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 7 Oct 2016 10:25:54 +0200 +Subject: [PATCH 7/9] ARM: dts: sun8i: Enable sun8i-emac on the Orange PI One + +The sun8i-emac hardware is present on the Orange PI One. +It uses the internal PHY. + +This patch create the needed emac node. + +Signed-off-by: Hans de Goede +Signed-off-by: Corentin Labbe +--- + arch/arm/boot/dts/sun8i-h3-orangepi-one.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +index 0adf932..25f2455 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +@@ -54,6 +54,7 @@ + + aliases { + serial0 = &uart0; ++ ethernet0 = &emac; + }; + + chosen { +@@ -94,6 +95,13 @@ + status = "okay"; + }; + ++&emac { ++ phy-handle = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ status = "okay"; ++}; ++ + &mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; +-- +2.9.3 + +From dd8a48a9d7250d4bfc501dd5a4e8179ffac27d11 Mon Sep 17 00:00:00 2001 +From: Corentin LABBE +Date: Fri, 7 Oct 2016 10:25:55 +0200 +Subject: [PATCH 8/9] ARM: dts: sun8i: Enable sun8i-emac on the Orange Pi 2 + +The sun8i-emac hardware is present on the Orange PI 2. +It uses the internal PHY. + +This patch create the needed emac node. + +Signed-off-by: Corentin Labbe +--- + arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +index f93f5d1..5608eb4 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +@@ -54,6 +54,7 @@ + + aliases { + serial0 = &uart0; ++ ethernet0 = &emac; + }; + + chosen { +@@ -184,3 +185,10 @@ + usb1_vbus-supply = <®_usb1_vbus>; + status = "okay"; + }; ++ ++&emac { ++ phy-handle = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ status = "okay"; ++}; +-- +2.9.3 + +From cc1f0d509ecebebe8b50c97b71c84fb082ce0e56 Mon Sep 17 00:00:00 2001 +From: Corentin LABBE +Date: Wed, 5 Oct 2016 11:48:24 +0200 +Subject: [PATCH 9/9] ARM: dt: sun8i-h3: Add sunxi-sid to dts for sun8i-h3 + +This patch add support for the sunxi-sid driver to the device tree for sun8i-h3. + +Signed-off-by: Corentin Labbe +--- + arch/arm/boot/dts/sun8i-h3.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi +index 54174cf..8628b38 100644 +--- a/arch/arm/boot/dts/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/sun8i-h3.dtsi +@@ -211,6 +211,11 @@ + #size-cells = <0>; + }; + ++ sid: eeprom@01c14200 { ++ compatible = "allwinner,sun7i-a20-sid"; ++ reg = <0x01c14200 0x200>; ++ }; ++ + usbphy: phy@01c19400 { + compatible = "allwinner,sun8i-h3-usb-phy"; + reg = <0x01c19400 0x2c>, +-- +2.9.3 + diff --git a/Fix-tegra-to-use-stdout-path-for-serial-console.patch b/Fix-tegra-to-use-stdout-path-for-serial-console.patch new file mode 100644 index 000000000..80a2d1b95 --- /dev/null +++ b/Fix-tegra-to-use-stdout-path-for-serial-console.patch @@ -0,0 +1,318 @@ +From 15b8caef5f380d9465876478ff5e365bc6afa5b6 Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Sun, 6 Mar 2016 10:59:13 +0000 +Subject: [PATCH] Fix tegra to use stdout-path for serial console + +--- + arch/arm/boot/dts/tegra114-dalmore.dts | 4 ++++ + arch/arm/boot/dts/tegra124-jetson-tk1.dts | 4 ++++ + arch/arm/boot/dts/tegra124-nyan.dtsi | 4 ++++ + arch/arm/boot/dts/tegra124-venice2.dts | 4 ++++ + arch/arm/boot/dts/tegra20-harmony.dts | 4 ++++ + arch/arm/boot/dts/tegra20-iris-512.dts | 4 ++++ + arch/arm/boot/dts/tegra20-medcom-wide.dts | 4 ++++ + arch/arm/boot/dts/tegra20-paz00.dts | 4 ++++ + arch/arm/boot/dts/tegra20-seaboard.dts | 4 ++++ + arch/arm/boot/dts/tegra20-tamonten.dtsi | 4 ++++ + arch/arm/boot/dts/tegra20-trimslice.dts | 4 ++++ + arch/arm/boot/dts/tegra20-ventana.dts | 4 ++++ + arch/arm/boot/dts/tegra20-whistler.dts | 4 ++++ + arch/arm/boot/dts/tegra30-apalis-eval.dts | 4 ++++ + arch/arm/boot/dts/tegra30-beaver.dts | 4 ++++ + arch/arm/boot/dts/tegra30-cardhu.dtsi | 4 ++++ + arch/arm/boot/dts/tegra30-colibri-eval-v3.dts | 4 ++++ + arch/arm64/boot/dts/nvidia/tegra132-norrin.dts | 5 ++++- + arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi | 4 ++++ + 19 files changed, 76 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts +index 8b7aa0d..b5748ee 100644 +--- a/arch/arm/boot/dts/tegra114-dalmore.dts ++++ b/arch/arm/boot/dts/tegra114-dalmore.dts +@@ -18,6 +18,10 @@ + serial0 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x80000000 0x40000000>; + }; +diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts +index 66b4451..abf046a 100644 +--- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts ++++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts +@@ -15,6 +15,10 @@ + serial0 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x0 0x80000000 0x0 0x80000000>; + }; +diff --git a/arch/arm/boot/dts/tegra124-nyan.dtsi b/arch/arm/boot/dts/tegra124-nyan.dtsi +index ec1aa64..e2cd39e 100644 +--- a/arch/arm/boot/dts/tegra124-nyan.dtsi ++++ b/arch/arm/boot/dts/tegra124-nyan.dtsi +@@ -8,6 +8,10 @@ + serial0 = &uarta; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x0 0x80000000 0x0 0x80000000>; + }; +diff --git a/arch/arm/boot/dts/tegra124-venice2.dts b/arch/arm/boot/dts/tegra124-venice2.dts +index cfbdf42..604f4b7 100644 +--- a/arch/arm/boot/dts/tegra124-venice2.dts ++++ b/arch/arm/boot/dts/tegra124-venice2.dts +@@ -13,6 +13,10 @@ + serial0 = &uarta; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x0 0x80000000 0x0 0x80000000>; + }; +diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts +index b926a07..4b73c76 100644 +--- a/arch/arm/boot/dts/tegra20-harmony.dts ++++ b/arch/arm/boot/dts/tegra20-harmony.dts +@@ -13,6 +13,10 @@ + serial0 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x00000000 0x40000000>; + }; +diff --git a/arch/arm/boot/dts/tegra20-iris-512.dts b/arch/arm/boot/dts/tegra20-iris-512.dts +index 1dd7d7b..bb56dfe 100644 +--- a/arch/arm/boot/dts/tegra20-iris-512.dts ++++ b/arch/arm/boot/dts/tegra20-iris-512.dts +@@ -11,6 +11,10 @@ + serial1 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + host1x@50000000 { + hdmi@54280000 { + status = "okay"; +diff --git a/arch/arm/boot/dts/tegra20-medcom-wide.dts b/arch/arm/boot/dts/tegra20-medcom-wide.dts +index 9b87526..34c6588 100644 +--- a/arch/arm/boot/dts/tegra20-medcom-wide.dts ++++ b/arch/arm/boot/dts/tegra20-medcom-wide.dts +@@ -10,6 +10,10 @@ + serial0 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + pwm@7000a000 { + status = "okay"; + }; +diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts +index ed7e100..81a10a9 100644 +--- a/arch/arm/boot/dts/tegra20-paz00.dts ++++ b/arch/arm/boot/dts/tegra20-paz00.dts +@@ -14,6 +14,10 @@ + serial1 = &uartc; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x00000000 0x20000000>; + }; +diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts +index aea8994..0aed748 100644 +--- a/arch/arm/boot/dts/tegra20-seaboard.dts ++++ b/arch/arm/boot/dts/tegra20-seaboard.dts +@@ -13,6 +13,10 @@ + serial0 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x00000000 0x40000000>; + }; +diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi +index 13d4e61..025e9e8 100644 +--- a/arch/arm/boot/dts/tegra20-tamonten.dtsi ++++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi +@@ -10,6 +10,10 @@ + serial0 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x00000000 0x20000000>; + }; +diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts +index d99af4e..69d25ca 100644 +--- a/arch/arm/boot/dts/tegra20-trimslice.dts ++++ b/arch/arm/boot/dts/tegra20-trimslice.dts +@@ -13,6 +13,10 @@ + serial0 = &uarta; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x00000000 0x40000000>; + }; +diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts +index 04c58e9..c61533a 100644 +--- a/arch/arm/boot/dts/tegra20-ventana.dts ++++ b/arch/arm/boot/dts/tegra20-ventana.dts +@@ -13,6 +13,10 @@ + serial0 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x00000000 0x40000000>; + }; +diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts +index 340d811..bd76585 100644 +--- a/arch/arm/boot/dts/tegra20-whistler.dts ++++ b/arch/arm/boot/dts/tegra20-whistler.dts +@@ -13,6 +13,10 @@ + serial0 = &uarta; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x00000000 0x20000000>; + }; +diff --git a/arch/arm/boot/dts/tegra30-apalis-eval.dts b/arch/arm/boot/dts/tegra30-apalis-eval.dts +index f2879cf..b914bcb 100644 +--- a/arch/arm/boot/dts/tegra30-apalis-eval.dts ++++ b/arch/arm/boot/dts/tegra30-apalis-eval.dts +@@ -17,6 +17,10 @@ + serial3 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + pcie-controller@00003000 { + status = "okay"; + +diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts +index 3dede39..1eca3b2 100644 +--- a/arch/arm/boot/dts/tegra30-beaver.dts ++++ b/arch/arm/boot/dts/tegra30-beaver.dts +@@ -12,6 +12,10 @@ + serial0 = &uarta; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x80000000 0x7ff00000>; + }; +diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi +index bb1ca15..de9d6cc 100644 +--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi ++++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi +@@ -35,6 +35,10 @@ + serial1 = &uartc; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + reg = <0x80000000 0x40000000>; + }; +diff --git a/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts b/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts +index 3ff019f..93e1ffd 100644 +--- a/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts ++++ b/arch/arm/boot/dts/tegra30-colibri-eval-v3.dts +@@ -15,6 +15,10 @@ + serial2 = &uartd; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + host1x@50000000 { + dc@54200000 { + rgb { +diff --git a/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts b/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts +index 62f33fc..3c0b4d7 100644 +--- a/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts ++++ b/arch/arm64/boot/dts/nvidia/tegra132-norrin.dts +@@ -10,9 +10,12 @@ + aliases { + rtc0 = "/i2c@0,7000d000/as3722@40"; + rtc1 = "/rtc@0,7000e000"; ++ serial0 = &uarta; + }; + +- chosen { }; ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; + + memory { + device_type = "memory"; +diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi +index ece0dec..73ba582 100644 +--- a/arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi ++++ b/arch/arm64/boot/dts/nvidia/tegra210-p2530.dtsi +@@ -9,6 +9,10 @@ + serial0 = &uarta; + }; + ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ + memory { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0xc0000000>; +-- +2.5.0 + diff --git a/HID-chicony-Add-support-for-Acer-Aspire-Switch-12.patch b/HID-chicony-Add-support-for-Acer-Aspire-Switch-12.patch new file mode 100644 index 000000000..2baf72e4c --- /dev/null +++ b/HID-chicony-Add-support-for-Acer-Aspire-Switch-12.patch @@ -0,0 +1,103 @@ +From 98f07385212073536f303c07ece455acdd4d267f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B9=20=D0=9A=D1=83?= + =?UTF-8?q?=D0=B4=D1=80=D1=8F=D0=B2=D1=86=D0=B5=D0=B2?= + +Date: Tue, 21 Jul 2015 13:31:52 +0300 +Subject: [PATCH] HID: chicony: Add support for Acer Aspire Switch 12 + +Acer Aspire Switch 12 keyboard Chicony's controller reports too big usage +index on the 1st interface. The patch fixes the report. The work based on +solution from drivers/hid/hid-holtek-mouse.c + +Bug report: https://bugzilla.kernel.org/show_bug.cgi?id=101721 + +Signed-off-by: Nicholas Kudriavtsev +Signed-off-by: Jiri Kosina +--- + drivers/hid/hid-chicony.c | 26 ++++++++++++++++++++++++++ + drivers/hid/hid-core.c | 1 + + drivers/hid/hid-ids.h | 1 + + 3 files changed, 28 insertions(+) + +diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c +index b613d5a79684..bc3cec199fee 100644 +--- a/drivers/hid/hid-chicony.c ++++ b/drivers/hid/hid-chicony.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include "hid-ids.h" + +@@ -57,10 +58,34 @@ static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + return 1; + } + ++static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc, ++ unsigned int *rsize) ++{ ++ struct usb_interface *intf = to_usb_interface(hdev->dev.parent); ++ ++ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { ++ /* Change usage maximum and logical maximum from 0x7fff to ++ * 0x2fff, so they don't exceed HID_MAX_USAGES */ ++ switch (hdev->product) { ++ case USB_DEVICE_ID_CHICONY_ACER_SWITCH12: ++ if (*rsize >= 128 && rdesc[64] == 0xff && rdesc[65] == 0x7f ++ && rdesc[69] == 0xff && rdesc[70] == 0x7f) { ++ hid_info(hdev, "Fixing up report descriptor\n"); ++ rdesc[65] = rdesc[70] = 0x2f; ++ } ++ break; ++ } ++ ++ } ++ return rdesc; ++} ++ ++ + static const struct hid_device_id ch_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) }, + { } + }; + MODULE_DEVICE_TABLE(hid, ch_devices); +@@ -68,6 +93,7 @@ MODULE_DEVICE_TABLE(hid, ch_devices); + static struct hid_driver ch_driver = { + .name = "chicony", + .id_table = ch_devices, ++ .report_fixup = ch_switch12_report_fixup, + .input_mapping = ch_input_mapping, + }; + module_hid_driver(ch_driver); +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index e6fce23b121a..f90ca6574221 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -1807,6 +1807,7 @@ static const struct hid_device_id hid_have_special_driver[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index b3b225b75d0a..79210ca8eff1 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -233,6 +233,7 @@ + #define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053 + #define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123 + #define USB_DEVICE_ID_CHICONY_AK1D 0x1125 ++#define USB_DEVICE_ID_CHICONY_ACER_SWITCH12 0x1421 + + #define USB_VENDOR_ID_CHUNGHWAT 0x2247 + #define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001 +-- +2.4.3 + diff --git a/HID-hid-input-Fix-accessing-freed-memory-during-devi.patch b/HID-hid-input-Fix-accessing-freed-memory-during-devi.patch new file mode 100644 index 000000000..6a64910b5 --- /dev/null +++ b/HID-hid-input-Fix-accessing-freed-memory-during-devi.patch @@ -0,0 +1,53 @@ +From 0621809e37936e7c2b3eac9165cf2aad7f9189eb Mon Sep 17 00:00:00 2001 +From: Krzysztof Kozlowski +Date: Mon, 3 Aug 2015 14:57:30 +0900 +Subject: [PATCH] HID: hid-input: Fix accessing freed memory during device + disconnect + +During unbinding the driver was dereferencing a pointer to memory +already freed by power_supply_unregister(). + +Driver was freeing its internal description of battery through pointers +stored in power_supply structure. However, because the core owns the +power supply instance, after calling power_supply_unregister() this +memory is freed and the driver cannot access these members. + +Fix this by storing the pointer to internal description of battery in a +local variable before calling power_supply_unregister(), so the pointer +remains valid. + +Signed-off-by: Krzysztof Kozlowski +Reported-by: H.J. Lu +Fixes: 297d716f6260 ("power_supply: Change ownership from driver to core") +Cc: +Reviewed-by: Dmitry Torokhov +Signed-off-by: Jiri Kosina +--- + drivers/hid/hid-input.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c +index 3511bbab..e3c6364 100644 +--- a/drivers/hid/hid-input.c ++++ b/drivers/hid/hid-input.c +@@ -462,12 +462,15 @@ out: + + static void hidinput_cleanup_battery(struct hid_device *dev) + { ++ const struct power_supply_desc *psy_desc; ++ + if (!dev->battery) + return; + ++ psy_desc = dev->battery->desc; + power_supply_unregister(dev->battery); +- kfree(dev->battery->desc->name); +- kfree(dev->battery->desc); ++ kfree(psy_desc->name); ++ kfree(psy_desc); + dev->battery = NULL; + } + #else /* !CONFIG_HID_BATTERY_STRENGTH */ +-- +2.4.3 + diff --git a/HID-wacom-fix-Bamboo-ONE-oops.patch b/HID-wacom-fix-Bamboo-ONE-oops.patch new file mode 100644 index 000000000..09e83179a --- /dev/null +++ b/HID-wacom-fix-Bamboo-ONE-oops.patch @@ -0,0 +1,46 @@ +From 580549ef6b3e3fb3b958de490ca99f43a089a2cf Mon Sep 17 00:00:00 2001 +From: Benjamin Tissoires +Date: Fri, 25 Mar 2016 15:26:55 +0100 +Subject: [PATCH] HID: wacom: fix Bamboo ONE oops + +Looks like recent changes in the Wacom driver made the Bamboo ONE crashes. +The tablet behaves as if it was a regular Bamboo device with pen, touch +and pad, but there is no physical pad connected to it. +The weird part is that the pad is still sending events and given that +there is no input node connected to it, we get anull pointer exception. + +Link: https://bugzilla.redhat.com/show_bug.cgi?id=1317116 + +Signed-off-by: Benjamin Tissoires +Acked-by: Ping Cheng +Cc: stable@vger.kernel.org +Signed-off-by: Jiri Kosina +--- + drivers/hid/wacom_wac.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c +index bd198bbd4df0..02c4efea241c 100644 +--- a/drivers/hid/wacom_wac.c ++++ b/drivers/hid/wacom_wac.c +@@ -2426,6 +2426,17 @@ void wacom_setup_device_quirks(struct wacom *wacom) + } + + /* ++ * Hack for the Bamboo One: ++ * the device presents a PAD/Touch interface as most Bamboos and even ++ * sends ghosts PAD data on it. However, later, we must disable this ++ * ghost interface, and we can not detect it unless we set it here ++ * to WACOM_DEVICETYPE_PAD or WACOM_DEVICETYPE_TOUCH. ++ */ ++ if (features->type == BAMBOO_PEN && ++ features->pktlen == WACOM_PKGLEN_BBTOUCH3) ++ features->device_type |= WACOM_DEVICETYPE_PAD; ++ ++ /* + * Raw Wacom-mode pen and touch events both come from interface + * 0, whose HID descriptor has an application usage of 0xFF0D + * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back +-- +2.5.5 + diff --git a/Initial-AllWinner-A64-and-PINE64-support.patch b/Initial-AllWinner-A64-and-PINE64-support.patch new file mode 100644 index 000000000..d21cbc1ca --- /dev/null +++ b/Initial-AllWinner-A64-and-PINE64-support.patch @@ -0,0 +1,1882 @@ +From 97f002d28e975991226ab70599731bd2ccc8c060 Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Sun, 6 Mar 2016 12:06:41 +0000 +Subject: [PATCH] Initial AllWinner A64 and PINE64 support + +--- + Documentation/devicetree/bindings/arm/sunxi.txt | 1 + + Documentation/devicetree/bindings/clock/sunxi.txt | 7 + + .../bindings/pinctrl/allwinner,sunxi-pinctrl.txt | 1 + + .../devicetree/bindings/vendor-prefixes.txt | 1 + + arch/arm/boot/dts/sun8i-h3.dtsi | 18 +- + arch/arm/mach-sunxi/Kconfig | 7 + + arch/arm64/Kconfig.platforms | 6 + + arch/arm64/boot/dts/Makefile | 1 + + arch/arm64/boot/dts/allwinner/Makefile | 5 + + .../dts/allwinner/sun50i-a64-pine64-common.dtsi | 80 +++ + .../boot/dts/allwinner/sun50i-a64-pine64-plus.dts | 59 ++ + .../arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 58 ++ + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 624 +++++++++++++++++++++ + drivers/clk/sunxi/Makefile | 1 + + drivers/clk/sunxi/clk-factors.c | 3 +- + drivers/clk/sunxi/clk-factors.h | 1 + + drivers/clk/sunxi/clk-multi-gates.c | 105 ++++ + drivers/clk/sunxi/clk-sunxi.c | 4 +- + drivers/crypto/Kconfig | 2 +- + drivers/pinctrl/sunxi/Kconfig | 4 + + drivers/pinctrl/sunxi/Makefile | 1 + + drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 602 ++++++++++++++++++++ + drivers/rtc/Kconfig | 7 +- + 23 files changed, 1582 insertions(+), 16 deletions(-) + create mode 100644 arch/arm64/boot/dts/allwinner/Makefile + create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-common.dtsi + create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts + create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts + create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi + create mode 100644 drivers/clk/sunxi/clk-multi-gates.c + create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c + +diff --git a/Documentation/devicetree/bindings/arm/sunxi.txt b/Documentation/devicetree/bindings/arm/sunxi.txt +index bb9b0faa..8b39d2b 100644 +--- a/Documentation/devicetree/bindings/arm/sunxi.txt ++++ b/Documentation/devicetree/bindings/arm/sunxi.txt +@@ -13,3 +13,4 @@ using one of the following compatible strings: + allwinner,sun8i-a33 + allwinner,sun8i-h3 + allwinner,sun9i-a80 ++ allwinner,sun50i-a64 +diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt +index e59f57b..8af12b5 100644 +--- a/Documentation/devicetree/bindings/clock/sunxi.txt ++++ b/Documentation/devicetree/bindings/clock/sunxi.txt +@@ -77,6 +77,8 @@ Required properties: + "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80 + "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80 + "allwinner,sun4i-a10-ve-clk" - for the Video Engine clock ++ "allwinner,sunxi-multi-bus-gates-clk" - for the multi-parent bus gates ++ "allwinner,sun50i-a64-bus-gates-clk" - for the bus gates on A64 + + Required properties for all clocks: + - reg : shall be the control register address for the clock. +@@ -117,6 +119,11 @@ For "allwinner,sun6i-a31-pll6-clk", there are 2 outputs. The first output + is the normal PLL6 output, or "pll6". The second output is rate doubled + PLL6, or "pll6x2". + ++The "allwinner,sunxi-multi-bus-gates-clk" holds the actual clocks in ++child nodes, where each one specifies the parent clock that the particular ++gates are depending from. The child nodes each follow the common clock ++binding as described in this document. ++ + The "allwinner,*-mmc-clk" clocks have three different outputs: the + main clock, with the ID 0, and the output and sample clocks, with the + IDs 1 and 2, respectively. +diff --git a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt +index 9213b27..08b2361 100644 +--- a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt ++++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt +@@ -21,6 +21,7 @@ Required properties: + "allwinner,sun9i-a80-r-pinctrl" + "allwinner,sun8i-a83t-pinctrl" + "allwinner,sun8i-h3-pinctrl" ++ "allwinner,sun50i-a64-pinctrl" + + - reg: Should contain the register physical address and length for the + pin controller. +diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt +index 72e2c5a..0c22fa9 100644 +--- a/Documentation/devicetree/bindings/vendor-prefixes.txt ++++ b/Documentation/devicetree/bindings/vendor-prefixes.txt +@@ -175,6 +175,7 @@ parade Parade Technologies Inc. + pericom Pericom Technology Inc. + phytec PHYTEC Messtechnik GmbH + picochip Picochip Ltd ++pine64 Pine64 + plathome Plat'Home Co., Ltd. + plda PLDA + pixcir PIXCIR MICROELECTRONICS Co., Ltd +diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi +index 1524130e..7c50fa0 100644 +--- a/arch/arm/boot/dts/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/sun8i-h3.dtsi +@@ -137,12 +137,12 @@ + clock-output-names = "pll6d2"; + }; + +- /* dummy clock until pll6 can be reused */ +- pll8: pll8_clk { +- #clock-cells = <0>; +- compatible = "fixed-clock"; +- clock-frequency = <1>; +- clock-output-names = "pll8"; ++ pll8: clk@c01c20044 { ++ #clock-cells = <1>; ++ compatible = "allwinner,sun6i-a31-pll6-clk"; ++ reg = <0x01c20044 0x4>; ++ clocks = <&osc24M>; ++ clock-output-names = "pll8", "pll8x2"; + }; + + cpu: cpu_clk@01c20050 { +@@ -243,7 +243,7 @@ + #clock-cells = <1>; + compatible = "allwinner,sun4i-a10-mmc-clk"; + reg = <0x01c20088 0x4>; +- clocks = <&osc24M>, <&pll6 0>, <&pll8>; ++ clocks = <&osc24M>, <&pll6 0>, <&pll8 0>; + clock-output-names = "mmc0", + "mmc0_output", + "mmc0_sample"; +@@ -253,7 +253,7 @@ + #clock-cells = <1>; + compatible = "allwinner,sun4i-a10-mmc-clk"; + reg = <0x01c2008c 0x4>; +- clocks = <&osc24M>, <&pll6 0>, <&pll8>; ++ clocks = <&osc24M>, <&pll6 0>, <&pll8 0>; + clock-output-names = "mmc1", + "mmc1_output", + "mmc1_sample"; +@@ -263,7 +263,7 @@ + #clock-cells = <1>; + compatible = "allwinner,sun4i-a10-mmc-clk"; + reg = <0x01c20090 0x4>; +- clocks = <&osc24M>, <&pll6 0>, <&pll8>; ++ clocks = <&osc24M>, <&pll6 0>, <&pll8 0>; + clock-output-names = "mmc2", + "mmc2_output", + "mmc2_sample"; +diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig +index c124d65..b305f5b 100644 +--- a/arch/arm/mach-sunxi/Kconfig ++++ b/arch/arm/mach-sunxi/Kconfig +@@ -46,4 +46,11 @@ config MACH_SUN9I + default ARCH_SUNXI + select ARM_GIC + ++config MACH_SUN50I ++ bool "Allwinner A64 (sun50i) SoCs support" ++ default ARCH_SUNXI ++ select ARM_GIC ++ select HAVE_ARM_ARCH_TIMER ++ select PINCTRL_SUN50I_A64 ++ + endif +diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms +index 21074f6..63a690d 100644 +--- a/arch/arm64/Kconfig.platforms ++++ b/arch/arm64/Kconfig.platforms +@@ -72,6 +72,12 @@ config ARCH_SEATTLE + config ARCH_SHMOBILE + bool + ++config ARCH_SUNXI ++ bool "Allwinner sunxi 64-bit SoC Family" ++ select PINCTRL_SUN50I_A64 ++ help ++ This enables support for Allwinner sunxi based SoCs like the A64. ++ + config ARCH_RENESAS + bool "Renesas SoC Platforms" + select ARCH_SHMOBILE +diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile +index f832b8a..3b7428a 100644 +--- a/arch/arm64/boot/dts/Makefile ++++ b/arch/arm64/boot/dts/Makefile +@@ -1,3 +1,4 @@ ++dts-dirs += allwinner + dts-dirs += altera + dts-dirs += amd + dts-dirs += apm +diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile +new file mode 100644 +index 0000000..1e29a5a +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/Makefile +@@ -0,0 +1,5 @@ ++dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-pine64-plus.dtb sun50i-a64-pine64.dtb ++ ++always := $(dtb-y) ++subdir-y := $(dts-dirs) ++clean-files := *.dtb +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-common.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-common.dtsi +new file mode 100644 +index 0000000..d5a7249 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-common.dtsi +@@ -0,0 +1,80 @@ ++/* ++ * Copyright (c) 2016 ARM Ltd. ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This library 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. ++ * ++ * Or, alternatively, ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++#include "sun50i-a64.dtsi" ++ ++/ { ++ ++ aliases { ++ serial0 = &uart0; ++ }; ++ ++ soc { ++ reg_vcc3v3: vcc3v3 { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc3v3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mmc0_pins>, <&mmc0_default_cd_pin>; ++ vmmc-supply = <®_vcc3v3>; ++ cd-gpios = <&pio 5 6 0>; ++ cd-inverted; ++ status = "okay"; ++}; ++ ++&uart0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins_a>; ++ status = "okay"; ++}; ++ ++&i2c1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts +new file mode 100644 +index 0000000..549dc15 +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64-plus.dts +@@ -0,0 +1,59 @@ ++/* ++ * Copyright (c) 2016 ARM Ltd. ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This library 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. ++ * ++ * Or, alternatively, ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++/dts-v1/; ++ ++#include "sun50i-a64-pine64-common.dtsi" ++ ++/ { ++ model = "Pine64+"; ++ compatible = "pine64,pine64-plus", "allwinner,sun50i-a64"; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ /* There is a model with 2GB of DRAM, but U-Boot fixes this for us. */ ++ memory { ++ reg = <0x40000000 0x40000000>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +new file mode 100644 +index 0000000..ebe029e +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2016 ARM Ltd. ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This library 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. ++ * ++ * Or, alternatively, ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++/dts-v1/; ++ ++#include "sun50i-a64-pine64-common.dtsi" ++ ++/ { ++ model = "Pine64"; ++ compatible = "pine64,pine64", "allwinner,sun50i-a64"; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ memory { ++ reg = <0x40000000 0x20000000>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +new file mode 100644 +index 0000000..1bd436f +--- /dev/null ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -0,0 +1,624 @@ ++/* ++ * Copyright (C) 2016 ARM Ltd. ++ * based on the Allwinner H3 dtsi: ++ * Copyright (C) 2015 Jens Kuske ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This file is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This file 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. ++ * ++ * Or, alternatively, ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++#include ++#include ++ ++/ { ++ interrupt-parent = <&gic>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a53", "arm,armv8"; ++ device_type = "cpu"; ++ reg = <0>; ++ enable-method = "psci"; ++ }; ++ ++ cpu@1 { ++ compatible = "arm,cortex-a53", "arm,armv8"; ++ device_type = "cpu"; ++ reg = <1>; ++ enable-method = "psci"; ++ }; ++ ++ cpu@2 { ++ compatible = "arm,cortex-a53", "arm,armv8"; ++ device_type = "cpu"; ++ reg = <2>; ++ enable-method = "psci"; ++ }; ++ ++ cpu@3 { ++ compatible = "arm,cortex-a53", "arm,armv8"; ++ device_type = "cpu"; ++ reg = <3>; ++ enable-method = "psci"; ++ }; ++ }; ++ ++ psci { ++ compatible = "arm,psci-0.2"; ++ method = "smc"; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x40000000 0>; ++ }; ++ ++ gic: interrupt-controller@1c81000 { ++ compatible = "arm,gic-400"; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ ++ reg = <0x01c81000 0x1000>, ++ <0x01c82000 0x2000>, ++ <0x01c84000 0x2000>, ++ <0x01c86000 0x2000>; ++ interrupts = ; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = , ++ , ++ , ++ ; ++ }; ++ ++ clocks { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ osc24M: osc24M_clk { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <24000000>; ++ clock-output-names = "osc24M"; ++ }; ++ ++ osc32k: osc32k_clk { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <32768>; ++ clock-output-names = "osc32k"; ++ }; ++ ++ pll1: pll1_clk@1c20000 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun8i-a23-pll1-clk"; ++ reg = <0x01c20000 0x4>; ++ clocks = <&osc24M>; ++ clock-output-names = "pll1"; ++ }; ++ ++ pll6: pll6_clk@1c20028 { ++ #clock-cells = <1>; ++ compatible = "allwinner,sun6i-a31-pll6-clk"; ++ reg = <0x01c20028 0x4>; ++ clocks = <&osc24M>; ++ clock-output-names = "pll6", "pll6x2"; ++ }; ++ ++ pll6d2: pll6d2_clk { ++ #clock-cells = <0>; ++ compatible = "fixed-factor-clock"; ++ clock-div = <2>; ++ clock-mult = <1>; ++ clocks = <&pll6 0>; ++ clock-output-names = "pll6d2"; ++ }; ++ ++ pll7: pll7_clk@1c2002c { ++ #clock-cells = <1>; ++ compatible = "allwinner,sun6i-a31-pll6-clk"; ++ reg = <0x01c2002c 0x4>; ++ clocks = <&osc24M>; ++ clock-output-names = "pll7", "pll7x2"; ++ }; ++ ++ cpu: cpu_clk@1c20050 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-a10-cpu-clk"; ++ reg = <0x01c20050 0x4>; ++ clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll1>; ++ clock-output-names = "cpu"; ++ critical-clocks = <0>; ++ }; ++ ++ axi: axi_clk@1c20050 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-a10-axi-clk"; ++ reg = <0x01c20050 0x4>; ++ clocks = <&cpu>; ++ clock-output-names = "axi"; ++ }; ++ ++ ahb1: ahb1_clk@1c20054 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun6i-a31-ahb1-clk"; ++ reg = <0x01c20054 0x4>; ++ clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>; ++ clock-output-names = "ahb1"; ++ }; ++ ++ ahb2: ahb2_clk@1c2005c { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun8i-h3-ahb2-clk"; ++ reg = <0x01c2005c 0x4>; ++ clocks = <&ahb1>, <&pll6d2>; ++ clock-output-names = "ahb2"; ++ }; ++ ++ apb1: apb1_clk@1c20054 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-a10-apb0-clk"; ++ reg = <0x01c20054 0x4>; ++ clocks = <&ahb1>; ++ clock-output-names = "apb1"; ++ }; ++ ++ apb2: apb2_clk@1c20058 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-a10-apb1-clk"; ++ reg = <0x01c20058 0x4>; ++ clocks = <&osc32k>, <&osc24M>, <&pll6 1>, <&pll6 1>; ++ clock-output-names = "apb2"; ++ }; ++ ++ bus_gates: bus_gates_clk@1c20060 { ++ #clock-cells = <1>; ++ compatible = "allwinner,sun50i-a64-bus-gates-clk", ++ "allwinner,sunxi-multi-bus-gates-clk"; ++ reg = <0x01c20060 0x14>; ++ ahb1_parent { ++ clocks = <&ahb1>; ++ clock-indices = <1>, <5>, ++ <6>, <8>, ++ <9>, <10>, ++ <13>, <14>, ++ <18>, <19>, ++ <20>, <21>, ++ <23>, <24>, ++ <25>, <28>, ++ <32>, <35>, ++ <36>, <37>, ++ <40>, <43>, ++ <44>, <52>, ++ <53>, <54>, ++ <135>; ++ clock-output-names = "bus_mipidsi", "bus_ce", ++ "bus_dma", "bus_mmc0", ++ "bus_mmc1", "bus_mmc2", ++ "bus_nand", "bus_sdram", ++ "bus_ts", "bus_hstimer", ++ "bus_spi0", "bus_spi1", ++ "bus_otg", "bus_otg_ehci0", ++ "bus_ehci0", "bus_otg_ohci0", ++ "bus_ve", "bus_lcd0", ++ "bus_lcd1", "bus_deint", ++ "bus_csi", "bus_hdmi", ++ "bus_de", "bus_gpu", ++ "bus_msgbox", "bus_spinlock", ++ "bus_dbg"; ++ }; ++ ahb2_parent { ++ clocks = <&ahb2>; ++ clock-indices = <17>, <29>; ++ clock-output-names = "bus_gmac", "bus_ohci0"; ++ }; ++ apb1_parent { ++ clocks = <&apb1>; ++ clock-indices = <64>, <65>, ++ <69>, <72>, ++ <76>, <77>, ++ <78>; ++ clock-output-names = "bus_codec", "bus_spdif", ++ "bus_pio", "bus_ths", ++ "bus_i2s0", "bus_i2s1", ++ "bus_i2s2"; ++ }; ++ abp2_parent { ++ clocks = <&apb2>; ++ clock-indices = <96>, <97>, ++ <98>, <101>, ++ <112>, <113>, ++ <114>, <115>, ++ <116>; ++ clock-output-names = "bus_i2c0", "bus_i2c1", ++ "bus_i2c2", "bus_scr", ++ "bus_uart0", "bus_uart1", ++ "bus_uart2", "bus_uart3", ++ "bus_uart4"; ++ }; ++ }; ++ ++ mmc0_clk: mmc0_clk@1c20088 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; ++ reg = <0x01c20088 0x4>; ++ clocks = <&osc24M>, <&pll6 1>, <&pll7 1>; ++ clock-output-names = "mmc0"; ++ }; ++ ++ mmc1_clk: mmc1_clk@1c2008c { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; ++ reg = <0x01c2008c 0x4>; ++ clocks = <&osc24M>, <&pll6 1>, <&pll7 1>; ++ clock-output-names = "mmc1"; ++ }; ++ ++ mmc2_clk: mmc2_clk@1c20090 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun4i-a10-mod0-clk"; ++ reg = <0x01c20090 0x4>; ++ clocks = <&osc24M>, <&pll6 1>, <&pll7 1>; ++ clock-output-names = "mmc2"; ++ }; ++ }; ++ ++ soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ mmc0: mmc@1c0f000 { ++ compatible = "allwinner,sun50i-a64-mmc", ++ "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c0f000 0x1000>; ++ clocks = <&bus_gates 8>, <&mmc0_clk>, ++ <&mmc0_clk>, <&mmc0_clk>; ++ clock-names = "ahb", "mmc", ++ "output", "sample"; ++ resets = <&ahb_rst 8>; ++ reset-names = "ahb"; ++ interrupts = ; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ mmc1: mmc@1c10000 { ++ compatible = "allwinner,sun50i-a64-mmc", ++ "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c10000 0x1000>; ++ clocks = <&bus_gates 9>, <&mmc1_clk>, ++ <&mmc1_clk>, <&mmc1_clk>; ++ clock-names = "ahb", "mmc", ++ "output", "sample"; ++ resets = <&ahb_rst 9>; ++ reset-names = "ahb"; ++ interrupts = ; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ mmc2: mmc@1c11000 { ++ compatible = "allwinner,sun50i-a64-mmc", ++ "allwinner,sun5i-a13-mmc"; ++ reg = <0x01c11000 0x1000>; ++ clocks = <&bus_gates 10>, <&mmc2_clk>, ++ <&mmc2_clk>, <&mmc2_clk>; ++ clock-names = "ahb", "mmc", ++ "output", "sample"; ++ resets = <&ahb_rst 10>; ++ reset-names = "ahb"; ++ interrupts = ; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ pio: pinctrl@1c20800 { ++ compatible = "allwinner,sun50i-a64-pinctrl"; ++ reg = <0x01c20800 0x400>; ++ interrupts = , ++ , ++ ; ++ clocks = <&bus_gates 69>; ++ gpio-controller; ++ #gpio-cells = <3>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ ++ uart0_pins_a: uart0@0 { ++ allwinner,pins = "PB8", "PB9"; ++ allwinner,function = "uart0"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart0_pins_b: uart0@1 { ++ allwinner,pins = "PF2", "PF3"; ++ allwinner,function = "uart0"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart1_2pins: uart1_2@0 { ++ allwinner,pins = "PG6", "PG7"; ++ allwinner,function = "uart1"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart1_4pins: uart1_4@0 { ++ allwinner,pins = "PG6", "PG7", "PG8", "PG9"; ++ allwinner,function = "uart1"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart2_2pins: uart2_2@0 { ++ allwinner,pins = "PB0", "PB1"; ++ allwinner,function = "uart2"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart2_4pins: uart2_4@0 { ++ allwinner,pins = "PB0", "PB1", "PB2", "PB3"; ++ allwinner,function = "uart2"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart3_pins_a: uart3@0 { ++ allwinner,pins = "PD0", "PD1"; ++ allwinner,function = "uart3"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart3_2pins_b: uart3_2@1 { ++ allwinner,pins = "PH4", "PH5"; ++ allwinner,function = "uart3"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart3_4pins_b: uart3_4@1 { ++ allwinner,pins = "PH4", "PH5", "PH6", "PH7"; ++ allwinner,function = "uart3"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart4_2pins: uart4_2@0 { ++ allwinner,pins = "PD2", "PD3"; ++ allwinner,function = "uart4"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ uart4_4pins: uart4_4@0 { ++ allwinner,pins = "PD2", "PD3", "PD4", "PD5"; ++ allwinner,function = "uart4"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ mmc0_pins: mmc0@0 { ++ allwinner,pins = "PF0", "PF1", "PF2", "PF3", ++ "PF4", "PF5"; ++ allwinner,function = "mmc0"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ mmc0_default_cd_pin: mmc0_cd_pin@0 { ++ allwinner,pins = "PF6"; ++ allwinner,function = "gpio_in"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ mmc1_pins: mmc1@0 { ++ allwinner,pins = "PG0", "PG1", "PG2", "PG3", ++ "PG4", "PG5"; ++ allwinner,function = "mmc1"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ mmc2_pins: mmc2@0 { ++ allwinner,pins = "PC1", "PC5", "PC6", "PC8", ++ "PC9", "PC10"; ++ allwinner,function = "mmc2"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ i2c0_pins: i2c0_pins { ++ allwinner,pins = "PH0", "PH1"; ++ allwinner,function = "i2c0"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ i2c1_pins: i2c1_pins { ++ allwinner,pins = "PH2", "PH3"; ++ allwinner,function = "i2c1"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ ++ i2c2_pins: i2c2_pins { ++ allwinner,pins = "PE14", "PE15"; ++ allwinner,function = "i2c2"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++ }; ++ ++ ahb_rst: reset@1c202c0 { ++ #reset-cells = <1>; ++ compatible = "allwinner,sun6i-a31-clock-reset"; ++ reg = <0x01c202c0 0xc>; ++ }; ++ ++ apb1_rst: reset@1c202d0 { ++ #reset-cells = <1>; ++ compatible = "allwinner,sun6i-a31-clock-reset"; ++ reg = <0x01c202d0 0x4>; ++ }; ++ ++ apb2_rst: reset@1c202d8 { ++ #reset-cells = <1>; ++ compatible = "allwinner,sun6i-a31-clock-reset"; ++ reg = <0x01c202d8 0x4>; ++ }; ++ ++ uart0: serial@1c28000 { ++ compatible = "snps,dw-apb-uart"; ++ reg = <0x01c28000 0x400>; ++ interrupts = ; ++ reg-shift = <2>; ++ reg-io-width = <4>; ++ clocks = <&bus_gates 112>; ++ resets = <&apb2_rst 16>; ++ status = "disabled"; ++ }; ++ ++ uart1: serial@1c28400 { ++ compatible = "snps,dw-apb-uart"; ++ reg = <0x01c28400 0x400>; ++ interrupts = ; ++ reg-shift = <2>; ++ reg-io-width = <4>; ++ clocks = <&bus_gates 113>; ++ resets = <&apb2_rst 17>; ++ status = "disabled"; ++ }; ++ ++ uart2: serial@1c28800 { ++ compatible = "snps,dw-apb-uart"; ++ reg = <0x01c28800 0x400>; ++ interrupts = ; ++ reg-shift = <2>; ++ reg-io-width = <4>; ++ clocks = <&bus_gates 114>; ++ resets = <&apb2_rst 18>; ++ status = "disabled"; ++ }; ++ ++ uart3: serial@1c28c00 { ++ compatible = "snps,dw-apb-uart"; ++ reg = <0x01c28c00 0x400>; ++ interrupts = ; ++ reg-shift = <2>; ++ reg-io-width = <4>; ++ clocks = <&bus_gates 115>; ++ resets = <&apb2_rst 19>; ++ status = "disabled"; ++ }; ++ ++ uart4: serial@1c29000 { ++ compatible = "snps,dw-apb-uart"; ++ reg = <0x01c29000 0x400>; ++ interrupts = ; ++ reg-shift = <2>; ++ reg-io-width = <4>; ++ clocks = <&bus_gates 116>; ++ resets = <&apb2_rst 20>; ++ status = "disabled"; ++ }; ++ ++ rtc: rtc@1f00000 { ++ compatible = "allwinner,sun6i-a31-rtc"; ++ reg = <0x01f00000 0x54>; ++ interrupts = , ++ ; ++ }; ++ ++ i2c0: i2c@1c2ac00 { ++ compatible = "allwinner,sun6i-a31-i2c"; ++ reg = <0x01c2ac00 0x400>; ++ interrupts = ; ++ clocks = <&bus_gates 96>; ++ resets = <&apb2_rst 0>; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ i2c1: i2c@1c2b000 { ++ compatible = "allwinner,sun6i-a31-i2c"; ++ reg = <0x01c2b000 0x400>; ++ interrupts = ; ++ clocks = <&bus_gates 97>; ++ resets = <&apb2_rst 1>; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ i2c2: i2c@1c2b400 { ++ compatible = "allwinner,sun6i-a31-i2c"; ++ reg = <0x01c2b400 0x400>; ++ interrupts = ; ++ clocks = <&bus_gates 98>; ++ resets = <&apb2_rst 2>; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++}; +diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile +index 3fd7901..3a9dc31 100644 +--- a/drivers/clk/sunxi/Makefile ++++ b/drivers/clk/sunxi/Makefile +@@ -11,6 +11,7 @@ obj-y += clk-a10-ve.o + obj-y += clk-a20-gmac.o + obj-y += clk-mod0.o + obj-y += clk-simple-gates.o ++obj-y += clk-multi-gates.o + obj-y += clk-sun8i-bus-gates.o + obj-y += clk-sun8i-mbus.o + obj-y += clk-sun9i-core.o +diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c +index 59428db..607ba53 100644 +--- a/drivers/clk/sunxi/clk-factors.c ++++ b/drivers/clk/sunxi/clk-factors.c +@@ -184,7 +184,8 @@ struct clk *sunxi_factors_register(struct device_node *node, + if (data->name) + clk_name = data->name; + else +- of_property_read_string(node, "clock-output-names", &clk_name); ++ of_property_read_string_index(node, "clock-output-names", ++ data->name_idx, &clk_name); + + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) +diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h +index 171085a..cc89d1f 100644 +--- a/drivers/clk/sunxi/clk-factors.h ++++ b/drivers/clk/sunxi/clk-factors.h +@@ -26,6 +26,7 @@ struct factors_data { + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); + const char *name; ++ int name_idx; + }; + + struct clk_factors { +diff --git a/drivers/clk/sunxi/clk-multi-gates.c b/drivers/clk/sunxi/clk-multi-gates.c +new file mode 100644 +index 0000000..76e715a +--- /dev/null ++++ b/drivers/clk/sunxi/clk-multi-gates.c +@@ -0,0 +1,105 @@ ++/* ++ * Copyright (C) 2016 ARM Ltd. ++ * ++ * Based on clk-sun8i-bus-gates.c, which is: ++ * Copyright (C) 2015 Jens Kuske ++ * Based on clk-simple-gates.c, which is: ++ * Copyright 2015 Maxime Ripard ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static DEFINE_SPINLOCK(gates_lock); ++ ++static void __init sunxi_parse_parent(struct device_node *node, ++ struct clk_onecell_data *clk_data, ++ void __iomem *reg) ++{ ++ const char *parent = of_clk_get_parent_name(node, 0); ++ const char *clk_name; ++ struct property *prop; ++ struct clk *clk; ++ const __be32 *p; ++ int index, i = 0; ++ ++ of_property_for_each_u32(node, "clock-indices", prop, p, index) { ++ of_property_read_string_index(node, "clock-output-names", ++ i, &clk_name); ++ ++ clk = clk_register_gate(NULL, clk_name, parent, 0, ++ reg + 4 * (index / 32), index % 32, ++ 0, &gates_lock); ++ i++; ++ if (IS_ERR(clk)) { ++ pr_warn("could not register gate clock \"%s\"\n", ++ clk_name); ++ continue; ++ } ++ if (clk_data->clks[index]) ++ pr_warn("bus-gate clock %s: index #%d already registered as %s\n", ++ clk_name, index, "?"); ++ else ++ clk_data->clks[index] = clk; ++ } ++} ++ ++static void __init sunxi_multi_bus_gates_init(struct device_node *node) ++{ ++ struct clk_onecell_data *clk_data; ++ struct device_node *child; ++ struct property *prop; ++ struct resource res; ++ void __iomem *reg; ++ const __be32 *p; ++ int number = 0; ++ int index; ++ ++ reg = of_io_request_and_map(node, 0, of_node_full_name(node)); ++ if (IS_ERR(reg)) ++ return; ++ ++ clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); ++ if (!clk_data) ++ goto err_unmap; ++ ++ for_each_child_of_node(node, child) ++ of_property_for_each_u32(child, "clock-indices", prop, p, index) ++ number = max(number, index); ++ ++ clk_data->clks = kcalloc(number + 1, sizeof(struct clk *), GFP_KERNEL); ++ if (!clk_data->clks) ++ goto err_free_data; ++ ++ for_each_child_of_node(node, child) ++ sunxi_parse_parent(child, clk_data, reg); ++ ++ clk_data->clk_num = number + 1; ++ if (of_clk_add_provider(node, of_clk_src_onecell_get, clk_data)) ++ pr_err("registering bus-gate clock %s failed\n", node->name); ++ ++ return; ++ ++err_free_data: ++ kfree(clk_data); ++err_unmap: ++ iounmap(reg); ++ of_address_to_resource(node, 0, &res); ++ release_mem_region(res.start, resource_size(&res)); ++} ++ ++CLK_OF_DECLARE(sunxi_multi_bus_gates, "allwinner,sunxi-multi-bus-gates-clk", ++ sunxi_multi_bus_gates_init); +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index 5ba2188..ca59458 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -711,14 +711,14 @@ static const struct factors_data sun4i_pll6_data __initconst = { + .enable = 31, + .table = &sun4i_pll5_config, + .getter = sun4i_get_pll5_factors, +- .name = "pll6", ++ .name_idx = 2, + }; + + static const struct factors_data sun6i_a31_pll6_data __initconst = { + .enable = 31, + .table = &sun6i_a31_pll6_config, + .getter = sun6i_a31_get_pll6_factors, +- .name = "pll6x2", ++ .name_idx = 1, + }; + + static const struct factors_data sun5i_a13_ahb_data __initconst = { +diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig +index 07d4942..737200f 100644 +--- a/drivers/crypto/Kconfig ++++ b/drivers/crypto/Kconfig +@@ -487,7 +487,7 @@ config CRYPTO_DEV_IMGTEC_HASH + + config CRYPTO_DEV_SUN4I_SS + tristate "Support for Allwinner Security System cryptographic accelerator" +- depends on ARCH_SUNXI ++ depends on ARCH_SUNXI && !64BIT + select CRYPTO_MD5 + select CRYPTO_SHA1 + select CRYPTO_AES +diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig +index f8dbc8b..c1f970f 100644 +--- a/drivers/pinctrl/sunxi/Kconfig ++++ b/drivers/pinctrl/sunxi/Kconfig +@@ -64,4 +64,8 @@ config PINCTRL_SUN9I_A80_R + depends on RESET_CONTROLLER + select PINCTRL_SUNXI_COMMON + ++config PINCTRL_SUN50I_A64 ++ bool ++ select PINCTRL_SUNXI_COMMON ++ + endif +diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile +index ef82f22..0ca7681 100644 +--- a/drivers/pinctrl/sunxi/Makefile ++++ b/drivers/pinctrl/sunxi/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_PINCTRL_SUN7I_A20) += pinctrl-sun7i-a20.o + obj-$(CONFIG_PINCTRL_SUN8I_A23) += pinctrl-sun8i-a23.o + obj-$(CONFIG_PINCTRL_SUN8I_A23_R) += pinctrl-sun8i-a23-r.o + obj-$(CONFIG_PINCTRL_SUN8I_A33) += pinctrl-sun8i-a33.o ++obj-$(CONFIG_PINCTRL_SUN50I_A64) += pinctrl-sun50i-a64.o + obj-$(CONFIG_PINCTRL_SUN8I_A83T) += pinctrl-sun8i-a83t.o + obj-$(CONFIG_PINCTRL_SUN8I_H3) += pinctrl-sun8i-h3.o + obj-$(CONFIG_PINCTRL_SUN9I_A80) += pinctrl-sun9i-a80.o +diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c +new file mode 100644 +index 0000000..a53cc23 +--- /dev/null ++++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c +@@ -0,0 +1,602 @@ ++/* ++ * Allwinner A64 SoCs pinctrl driver. ++ * ++ * Copyright (C) 2016 - ARM Ltd. ++ * Author: Andre Przywara ++ * ++ * Based on pinctrl-sun7i-a20.c, which is: ++ * Copyright (C) 2014 Maxime Ripard ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "pinctrl-sunxi.h" ++ ++static const struct sunxi_desc_pin a64_pins[] = { ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart2"), /* TX */ ++ SUNXI_FUNCTION(0x4, "jtag"), /* MS0 */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)), /* EINT0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart2"), /* RX */ ++ SUNXI_FUNCTION(0x4, "jtag"), /* CK0 */ ++ SUNXI_FUNCTION(0x5, "sim"), /* VCCEN */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)), /* EINT1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart2"), /* RTS */ ++ SUNXI_FUNCTION(0x4, "jtag"), /* DO0 */ ++ SUNXI_FUNCTION(0x5, "sim"), /* VPPEN */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)), /* EINT2 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart2"), /* CTS */ ++ SUNXI_FUNCTION(0x3, "i2s0"), /* MCLK */ ++ SUNXI_FUNCTION(0x4, "jtag"), /* DI0 */ ++ SUNXI_FUNCTION(0x5, "sim"), /* VPPPP */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)), /* EINT3 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "aif2"), /* SYNC */ ++ SUNXI_FUNCTION(0x3, "i2s0"), /* SYNC */ ++ SUNXI_FUNCTION(0x5, "sim"), /* CLK */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)), /* EINT4 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "aif2"), /* BCLK */ ++ SUNXI_FUNCTION(0x3, "i2s0"), /* BCLK */ ++ SUNXI_FUNCTION(0x5, "sim"), /* DATA */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)), /* EINT5 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "aif2"), /* DOUT */ ++ SUNXI_FUNCTION(0x3, "i2s0"), /* DOUT */ ++ SUNXI_FUNCTION(0x5, "sim"), /* RST */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)), /* EINT6 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "aif2"), /* DIN */ ++ SUNXI_FUNCTION(0x3, "i2s0"), /* DIN */ ++ SUNXI_FUNCTION(0x5, "sim"), /* DET */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)), /* EINT7 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x4, "uart0"), /* TX */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)), /* EINT8 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x4, "uart0"), /* RX */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)), /* EINT9 */ ++ /* Hole */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NWE */ ++ SUNXI_FUNCTION(0x4, "spi0")), /* MOSI */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NALE */ ++ SUNXI_FUNCTION(0x3, "mmc2"), /* DS */ ++ SUNXI_FUNCTION(0x4, "spi0")), /* MISO */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NCLE */ ++ SUNXI_FUNCTION(0x4, "spi0")), /* SCK */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NCE1 */ ++ SUNXI_FUNCTION(0x4, "spi0")), /* CS */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0")), /* NCE0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NRE# */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NRB0 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0")), /* NRB1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQ0 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQ1 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQ2 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQ3 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQ4 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* D4 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQ5 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* D5 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQ6 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* D6 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQ7 */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* D7 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "nand0"), /* NDQS */ ++ SUNXI_FUNCTION(0x3, "mmc2")), /* RST */ ++ /* Hole */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */ ++ SUNXI_FUNCTION(0x3, "uart3"), /* TX */ ++ SUNXI_FUNCTION(0x4, "spi1"), /* CS */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* CLK */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */ ++ SUNXI_FUNCTION(0x3, "uart3"), /* RX */ ++ SUNXI_FUNCTION(0x4, "spi1"), /* CLK */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* DE */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */ ++ SUNXI_FUNCTION(0x3, "uart4"), /* TX */ ++ SUNXI_FUNCTION(0x4, "spi1"), /* MOSI */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* HSYNC */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */ ++ SUNXI_FUNCTION(0x3, "uart4"), /* RX */ ++ SUNXI_FUNCTION(0x4, "spi1"), /* MISO */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* VSYNC */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */ ++ SUNXI_FUNCTION(0x3, "uart4"), /* RTS */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* D0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */ ++ SUNXI_FUNCTION(0x3, "uart4"), /* CTS */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* D1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* D2 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* D3 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */ ++ SUNXI_FUNCTION(0x4, "emac"), /* ERXD3 */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* D4 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */ ++ SUNXI_FUNCTION(0x4, "emac"), /* ERXD2 */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* D5 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ERXD1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ERXD0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VP0 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ERXCK */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VN0 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ERXCTL */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VP1 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ENULL */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VN1 */ ++ SUNXI_FUNCTION(0x4, "emac"), /* ETXD3 */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* D6 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VP2 */ ++ SUNXI_FUNCTION(0x4, "emac"), /* ETXD2 */ ++ SUNXI_FUNCTION(0x5, "ccir")), /* D7 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VN2 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ETXD1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VPC */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ETXD0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* DE */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VNC */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ETXCK */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VP3 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ETXCTL */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */ ++ SUNXI_FUNCTION(0x3, "lvds0"), /* VN3 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* ECLKIN */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "pwm"), /* PWM0 */ ++ SUNXI_FUNCTION(0x4, "emac")), /* EMDC */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x4, "emac")), /* EMDIO */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out")), ++ /* Hole */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* PCK */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* CLK */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* CK */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* ERR */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* HSYNC */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* SYNC */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* VSYNC */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* DVLD */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* D0 */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* D0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* D1 */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* D1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* D2 */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* D2 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* D3 */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* D3 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* D4 */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* D4 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* D5 */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* D5 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* D6 */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* D6 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0"), /* D7 */ ++ SUNXI_FUNCTION(0x4, "ts0")), /* D7 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0")), /* SCK */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "csi0")), /* SDA */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "pll"), /* LOCK_DBG */ ++ SUNXI_FUNCTION(0x3, "i2c2")), /* SCK */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x3, "i2c2")), /* SDA */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out")), ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out")), ++ /* Hole */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */ ++ SUNXI_FUNCTION(0x3, "jtag")), /* MSI */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */ ++ SUNXI_FUNCTION(0x3, "jtag")), /* DI1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */ ++ SUNXI_FUNCTION(0x3, "uart0")), /* TX */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */ ++ SUNXI_FUNCTION(0x3, "jtag")), /* DO1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */ ++ SUNXI_FUNCTION(0x4, "uart0")), /* RX */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */ ++ SUNXI_FUNCTION(0x3, "jtag")), /* CK1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out")), ++ /* Hole */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)), /* EINT0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)), /* EINT1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc1"), /* D0 */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)), /* EINT2 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc1"), /* D1 */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)), /* EINT3 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc1"), /* D2 */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)), /* EINT4 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mmc1"), /* D3 */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)), /* EINT5 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart1"), /* TX */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)), /* EINT6 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart1"), /* RX */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)), /* EINT7 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)), /* EINT8 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)), /* EINT9 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "aif3"), /* SYNC */ ++ SUNXI_FUNCTION(0x3, "i2s1"), /* SYNC */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)), /* EINT10 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "aif3"), /* BCLK */ ++ SUNXI_FUNCTION(0x3, "i2s1"), /* BCLK */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)), /* EINT11 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "aif3"), /* DOUT */ ++ SUNXI_FUNCTION(0x3, "i2s1"), /* DOUT */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)), /* EINT12 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "aif3"), /* DIN */ ++ SUNXI_FUNCTION(0x3, "i2s1"), /* DIN */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)), /* EINT13 */ ++ /* Hole */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "i2c0"), /* SCK */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)), /* EINT0 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "i2c0"), /* SDA */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)), /* EINT1 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "i2c1"), /* SCK */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)), /* EINT2 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "i2c1"), /* SDA */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)), /* EINT3 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart3"), /* TX */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)), /* EINT4 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart3"), /* RX */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)), /* EINT5 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart3"), /* RTS */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)), /* EINT6 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "uart3"), /* CTS */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)), /* EINT7 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "spdif"), /* OUT */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)), /* EINT8 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)), /* EINT9 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mic"), /* CLK */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)), /* EINT10 */ ++ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11), ++ SUNXI_FUNCTION(0x0, "gpio_in"), ++ SUNXI_FUNCTION(0x1, "gpio_out"), ++ SUNXI_FUNCTION(0x2, "mic"), /* DATA */ ++ SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)), /* EINT11 */ ++}; ++ ++static const struct sunxi_pinctrl_desc a64_pinctrl_data = { ++ .pins = a64_pins, ++ .npins = ARRAY_SIZE(a64_pins), ++ .irq_banks = 3, ++}; ++ ++static int a64_pinctrl_probe(struct platform_device *pdev) ++{ ++ return sunxi_pinctrl_init(pdev, ++ &a64_pinctrl_data); ++} ++ ++static const struct of_device_id a64_pinctrl_match[] = { ++ { .compatible = "allwinner,sun50i-a64-pinctrl", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, a64_pinctrl_match); ++ ++static struct platform_driver a64_pinctrl_driver = { ++ .probe = a64_pinctrl_probe, ++ .driver = { ++ .name = "sun50i-a64-pinctrl", ++ .of_match_table = a64_pinctrl_match, ++ }, ++}; ++builtin_platform_driver(a64_pinctrl_driver); +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index 376322f..526eaf4 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1360,10 +1360,11 @@ config RTC_DRV_SUN4V + + config RTC_DRV_SUN6I + tristate "Allwinner A31 RTC" +- depends on MACH_SUN6I || MACH_SUN8I ++ default MACH_SUN6I || MACH_SUN8I ++ depends on ARCH_SUNXI + help +- If you say Y here you will get support for the RTC found on +- Allwinner A31. ++ If you say Y here you will get support for the RTC found in ++ some Allwinner SoCs like the A31 or the A64. + + config RTC_DRV_SUNXI + tristate "Allwinner sun4i/sun7i RTC" +-- +2.5.0 + diff --git a/Initialize-msg-shm-IPC-objects-before-doing-ipc_addi.patch b/Initialize-msg-shm-IPC-objects-before-doing-ipc_addi.patch new file mode 100644 index 000000000..8a53a43ce --- /dev/null +++ b/Initialize-msg-shm-IPC-objects-before-doing-ipc_addi.patch @@ -0,0 +1,117 @@ +From b9a532277938798b53178d5a66af6e2915cb27cf Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +Date: Wed, 30 Sep 2015 12:48:40 -0400 +Subject: [PATCH] Initialize msg/shm IPC objects before doing ipc_addid() + +As reported by Dmitry Vyukov, we really shouldn't do ipc_addid() before +having initialized the IPC object state. Yes, we initialize the IPC +object in a locked state, but with all the lockless RCU lookup work, +that IPC object lock no longer means that the state cannot be seen. + +We already did this for the IPC semaphore code (see commit e8577d1f0329: +"ipc/sem.c: fully initialize sem_array before making it visible") but we +clearly forgot about msg and shm. + +Reported-by: Dmitry Vyukov +Cc: Manfred Spraul +Cc: Davidlohr Bueso +Cc: stable@vger.kernel.org +Signed-off-by: Linus Torvalds +--- + ipc/msg.c | 14 +++++++------- + ipc/shm.c | 13 +++++++------ + ipc/util.c | 8 ++++---- + 3 files changed, 18 insertions(+), 17 deletions(-) + +diff --git a/ipc/msg.c b/ipc/msg.c +index 66c4f567eb73..1471db9a7e61 100644 +--- a/ipc/msg.c ++++ b/ipc/msg.c +@@ -137,13 +137,6 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) + return retval; + } + +- /* ipc_addid() locks msq upon success. */ +- id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); +- if (id < 0) { +- ipc_rcu_putref(msq, msg_rcu_free); +- return id; +- } +- + msq->q_stime = msq->q_rtime = 0; + msq->q_ctime = get_seconds(); + msq->q_cbytes = msq->q_qnum = 0; +@@ -153,6 +146,13 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) + INIT_LIST_HEAD(&msq->q_receivers); + INIT_LIST_HEAD(&msq->q_senders); + ++ /* ipc_addid() locks msq upon success. */ ++ id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); ++ if (id < 0) { ++ ipc_rcu_putref(msq, msg_rcu_free); ++ return id; ++ } ++ + ipc_unlock_object(&msq->q_perm); + rcu_read_unlock(); + +diff --git a/ipc/shm.c b/ipc/shm.c +index 222131e8e38f..41787276e141 100644 +--- a/ipc/shm.c ++++ b/ipc/shm.c +@@ -551,12 +551,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) + if (IS_ERR(file)) + goto no_file; + +- id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); +- if (id < 0) { +- error = id; +- goto no_id; +- } +- + shp->shm_cprid = task_tgid_vnr(current); + shp->shm_lprid = 0; + shp->shm_atim = shp->shm_dtim = 0; +@@ -565,6 +559,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) + shp->shm_nattch = 0; + shp->shm_file = file; + shp->shm_creator = current; ++ ++ id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); ++ if (id < 0) { ++ error = id; ++ goto no_id; ++ } ++ + list_add(&shp->shm_clist, ¤t->sysvshm.shm_clist); + + /* +diff --git a/ipc/util.c b/ipc/util.c +index be4230020a1f..0f401d94b7c6 100644 +--- a/ipc/util.c ++++ b/ipc/util.c +@@ -237,6 +237,10 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size) + rcu_read_lock(); + spin_lock(&new->lock); + ++ current_euid_egid(&euid, &egid); ++ new->cuid = new->uid = euid; ++ new->gid = new->cgid = egid; ++ + id = idr_alloc(&ids->ipcs_idr, new, + (next_id < 0) ? 0 : ipcid_to_idx(next_id), 0, + GFP_NOWAIT); +@@ -249,10 +253,6 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size) + + ids->in_use++; + +- current_euid_egid(&euid, &egid); +- new->cuid = new->uid = euid; +- new->gid = new->cgid = egid; +- + if (next_id < 0) { + new->seq = ids->seq++; + if (ids->seq > IPCID_SEQ_MAX) +-- +2.4.3 + diff --git a/KEYS-Fix-ASN.1-indefinite-length-object-parsing.patch b/KEYS-Fix-ASN.1-indefinite-length-object-parsing.patch new file mode 100644 index 000000000..957de0977 --- /dev/null +++ b/KEYS-Fix-ASN.1-indefinite-length-object-parsing.patch @@ -0,0 +1,91 @@ +From 23c8a812dc3c621009e4f0e5342aa4e2ede1ceaa Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 23 Feb 2016 11:03:12 +0000 +Subject: [PATCH] KEYS: Fix ASN.1 indefinite length object parsing + +This fixes CVE-2016-0758. + +In the ASN.1 decoder, when the length field of an ASN.1 value is extracted, +it isn't validated against the remaining amount of data before being added +to the cursor. With a sufficiently large size indicated, the check: + + datalen - dp < 2 + +may then fail due to integer overflow. + +Fix this by checking the length indicated against the amount of remaining +data in both places a definite length is determined. + +Whilst we're at it, make the following changes: + + (1) Check the maximum size of extended length does not exceed the capacity + of the variable it's being stored in (len) rather than the type that + variable is assumed to be (size_t). + + (2) Compare the EOC tag to the symbolic constant ASN1_EOC rather than the + integer 0. + + (3) To reduce confusion, move the initialisation of len outside of: + + for (len = 0; n > 0; n--) { + + since it doesn't have anything to do with the loop counter n. + +Signed-off-by: David Howells +Reviewed-by: Mimi Zohar +Acked-by: David Woodhouse +Acked-by: Peter Jones +--- + lib/asn1_decoder.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c +index 2b3f46c049d4..554522934c44 100644 +--- a/lib/asn1_decoder.c ++++ b/lib/asn1_decoder.c +@@ -74,7 +74,7 @@ next_tag: + + /* Extract a tag from the data */ + tag = data[dp++]; +- if (tag == 0) { ++ if (tag == ASN1_EOC) { + /* It appears to be an EOC. */ + if (data[dp++] != 0) + goto invalid_eoc; +@@ -96,10 +96,8 @@ next_tag: + + /* Extract the length */ + len = data[dp++]; +- if (len <= 0x7f) { +- dp += len; +- goto next_tag; +- } ++ if (len <= 0x7f) ++ goto check_length; + + if (unlikely(len == ASN1_INDEFINITE_LENGTH)) { + /* Indefinite length */ +@@ -110,14 +108,18 @@ next_tag: + } + + n = len - 0x80; +- if (unlikely(n > sizeof(size_t) - 1)) ++ if (unlikely(n > sizeof(len) - 1)) + goto length_too_long; + if (unlikely(n > datalen - dp)) + goto data_overrun_error; +- for (len = 0; n > 0; n--) { ++ len = 0; ++ for (; n > 0; n--) { + len <<= 8; + len |= data[dp++]; + } ++check_length: ++ if (len > datalen - dp) ++ goto data_overrun_error; + dp += len; + goto next_tag; + +-- +2.5.5 + diff --git a/MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch b/MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch index 05be7a028..81a58b6fe 100644 --- a/MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch +++ b/MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch @@ -121,6 +121,8 @@ index e2ec54e2b952..8dab549985d8 100644 $(obj)/configs.o: $(obj)/config_data.h # config_data.h contains the same information as ikconfig.h but gzipped. + # Info from config_data can be extracted from /proc/config* + targets += config_data.gz diff --git a/kernel/modsign_uefi.c b/kernel/modsign_uefi.c new file mode 100644 index 000000000000..fe4a6f2bf10a diff --git a/PatchList.txt b/PatchList.txt index 3a7fd7377..3cce44ad0 100644 --- a/PatchList.txt +++ b/PatchList.txt @@ -76,3 +76,4 @@ firmware-Drop-WARN-from-usermodehelper_read_trylock-.patch drm-i915-turn-off-wc-mmaps.patch +i8042-skip-selftest-asus-laptops.patch diff --git a/RDS-verify-the-underlying-transport-exists-before-cr.patch b/RDS-verify-the-underlying-transport-exists-before-cr.patch new file mode 100644 index 000000000..eb39c1f1d --- /dev/null +++ b/RDS-verify-the-underlying-transport-exists-before-cr.patch @@ -0,0 +1,79 @@ +From 74e98eb085889b0d2d4908f59f6e00026063014f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 8 Sep 2015 10:53:40 -0400 +Subject: [PATCH] RDS: verify the underlying transport exists before creating a + connection + +There was no verification that an underlying transport exists when creating +a connection, this would cause dereferencing a NULL ptr. + +It might happen on sockets that weren't properly bound before attempting to +send a message, which will cause a NULL ptr deref: + +[135546.047719] kasan: GPF could be caused by NULL-ptr deref or user memory accessgeneral protection fault: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC KASAN +[135546.051270] Modules linked in: +[135546.051781] CPU: 4 PID: 15650 Comm: trinity-c4 Not tainted 4.2.0-next-20150902-sasha-00041-gbaa1222-dirty #2527 +[135546.053217] task: ffff8800835bc000 ti: ffff8800bc708000 task.ti: ffff8800bc708000 +[135546.054291] RIP: __rds_conn_create (net/rds/connection.c:194) +[135546.055666] RSP: 0018:ffff8800bc70fab0 EFLAGS: 00010202 +[135546.056457] RAX: dffffc0000000000 RBX: 0000000000000f2c RCX: ffff8800835bc000 +[135546.057494] RDX: 0000000000000007 RSI: ffff8800835bccd8 RDI: 0000000000000038 +[135546.058530] RBP: ffff8800bc70fb18 R08: 0000000000000001 R09: 0000000000000000 +[135546.059556] R10: ffffed014d7a3a23 R11: ffffed014d7a3a21 R12: 0000000000000000 +[135546.060614] R13: 0000000000000001 R14: ffff8801ec3d0000 R15: 0000000000000000 +[135546.061668] FS: 00007faad4ffb700(0000) GS:ffff880252000000(0000) knlGS:0000000000000000 +[135546.062836] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b +[135546.063682] CR2: 000000000000846a CR3: 000000009d137000 CR4: 00000000000006a0 +[135546.064723] Stack: +[135546.065048] ffffffffafe2055c ffffffffafe23fc1 ffffed00493097bf ffff8801ec3d0008 +[135546.066247] 0000000000000000 00000000000000d0 0000000000000000 ac194a24c0586342 +[135546.067438] 1ffff100178e1f78 ffff880320581b00 ffff8800bc70fdd0 ffff880320581b00 +[135546.068629] Call Trace: +[135546.069028] ? __rds_conn_create (include/linux/rcupdate.h:856 net/rds/connection.c:134) +[135546.069989] ? rds_message_copy_from_user (net/rds/message.c:298) +[135546.071021] rds_conn_create_outgoing (net/rds/connection.c:278) +[135546.071981] rds_sendmsg (net/rds/send.c:1058) +[135546.072858] ? perf_trace_lock (include/trace/events/lock.h:38) +[135546.073744] ? lockdep_init (kernel/locking/lockdep.c:3298) +[135546.074577] ? rds_send_drop_to (net/rds/send.c:976) +[135546.075508] ? __might_fault (./arch/x86/include/asm/current.h:14 mm/memory.c:3795) +[135546.076349] ? __might_fault (mm/memory.c:3795) +[135546.077179] ? rds_send_drop_to (net/rds/send.c:976) +[135546.078114] sock_sendmsg (net/socket.c:611 net/socket.c:620) +[135546.078856] SYSC_sendto (net/socket.c:1657) +[135546.079596] ? SYSC_connect (net/socket.c:1628) +[135546.080510] ? trace_dump_stack (kernel/trace/trace.c:1926) +[135546.081397] ? ring_buffer_unlock_commit (kernel/trace/ring_buffer.c:2479 kernel/trace/ring_buffer.c:2558 kernel/trace/ring_buffer.c:2674) +[135546.082390] ? trace_buffer_unlock_commit (kernel/trace/trace.c:1749) +[135546.083410] ? trace_event_raw_event_sys_enter (include/trace/events/syscalls.h:16) +[135546.084481] ? do_audit_syscall_entry (include/trace/events/syscalls.h:16) +[135546.085438] ? trace_buffer_unlock_commit (kernel/trace/trace.c:1749) +[135546.085515] rds_ib_laddr_check(): addr 36.74.25.172 ret -99 node type -1 + +Acked-by: Santosh Shilimkar +Signed-off-by: Sasha Levin +Signed-off-by: David S. Miller +--- + net/rds/connection.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/rds/connection.c b/net/rds/connection.c +index 9b2de5e67d79..49adeef8090c 100644 +--- a/net/rds/connection.c ++++ b/net/rds/connection.c +@@ -190,6 +190,12 @@ new_conn: + } + } + ++ if (trans == NULL) { ++ kmem_cache_free(rds_conn_slab, conn); ++ conn = ERR_PTR(-ENODEV); ++ goto out; ++ } ++ + conn->c_trans = trans; + + ret = trans->conn_alloc(conn, gfp); +-- +2.4.3 + diff --git a/USB-usbfs-fix-potential-infoleak-in-devio.patch b/USB-usbfs-fix-potential-infoleak-in-devio.patch new file mode 100644 index 000000000..48360c930 --- /dev/null +++ b/USB-usbfs-fix-potential-infoleak-in-devio.patch @@ -0,0 +1,41 @@ +From 7adc5cbc25dcc47dc3856108d9823d08da75da9d Mon Sep 17 00:00:00 2001 +From: Kangjie Lu +Date: Tue, 3 May 2016 16:32:16 -0400 +Subject: [PATCH] USB: usbfs: fix potential infoleak in devio +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The stack object “ci” has a total size of 8 bytes. Its last 3 bytes +are padding bytes which are not initialized and leaked to userland +via “copy_to_user”. + +Signed-off-by: Kangjie Lu +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/core/devio.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c +index 52c4461dfccd..9b7f1f75e887 100644 +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -1316,10 +1316,11 @@ static int proc_getdriver(struct usb_dev_state *ps, void __user *arg) + + static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg) + { +- struct usbdevfs_connectinfo ci = { +- .devnum = ps->dev->devnum, +- .slow = ps->dev->speed == USB_SPEED_LOW +- }; ++ struct usbdevfs_connectinfo ci; ++ ++ memset(&ci, 0, sizeof(ci)); ++ ci.devnum = ps->dev->devnum; ++ ci.slow = ps->dev->speed == USB_SPEED_LOW; + + if (copy_to_user(arg, &ci, sizeof(ci))) + return -EFAULT; +-- +2.5.5 + diff --git a/USB-whiteheat-fix-potential-null-deref-at-probe.patch b/USB-whiteheat-fix-potential-null-deref-at-probe.patch new file mode 100644 index 000000000..00fd5578c --- /dev/null +++ b/USB-whiteheat-fix-potential-null-deref-at-probe.patch @@ -0,0 +1,81 @@ +From 10d98bced414c6fc1d09db123e7f762d91b5ebea Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Wed, 23 Sep 2015 11:41:42 -0700 +Subject: [PATCH] USB: whiteheat: fix potential null-deref at probe + +Fix potential null-pointer dereference at probe by making sure that the +required endpoints are present. + +The whiteheat driver assumes there are at least five pairs of bulk +endpoints, of which the final pair is used for the "command port". An +attempt to bind to an interface with fewer bulk endpoints would +currently lead to an oops. + +Fixes CVE-2015-5257. + +Reported-by: Moein Ghasemzadeh +Cc: stable +Signed-off-by: Johan Hovold +--- + drivers/usb/serial/whiteheat.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c +index 6c3734d2b45a..d3ea90bef84d 100644 +--- a/drivers/usb/serial/whiteheat.c ++++ b/drivers/usb/serial/whiteheat.c +@@ -80,6 +80,8 @@ static int whiteheat_firmware_download(struct usb_serial *serial, + static int whiteheat_firmware_attach(struct usb_serial *serial); + + /* function prototypes for the Connect Tech WhiteHEAT serial converter */ ++static int whiteheat_probe(struct usb_serial *serial, ++ const struct usb_device_id *id); + static int whiteheat_attach(struct usb_serial *serial); + static void whiteheat_release(struct usb_serial *serial); + static int whiteheat_port_probe(struct usb_serial_port *port); +@@ -116,6 +118,7 @@ static struct usb_serial_driver whiteheat_device = { + .description = "Connect Tech - WhiteHEAT", + .id_table = id_table_std, + .num_ports = 4, ++ .probe = whiteheat_probe, + .attach = whiteheat_attach, + .release = whiteheat_release, + .port_probe = whiteheat_port_probe, +@@ -217,6 +220,34 @@ static int whiteheat_firmware_attach(struct usb_serial *serial) + /***************************************************************************** + * Connect Tech's White Heat serial driver functions + *****************************************************************************/ ++ ++static int whiteheat_probe(struct usb_serial *serial, ++ const struct usb_device_id *id) ++{ ++ struct usb_host_interface *iface_desc; ++ struct usb_endpoint_descriptor *endpoint; ++ size_t num_bulk_in = 0; ++ size_t num_bulk_out = 0; ++ size_t min_num_bulk; ++ unsigned int i; ++ ++ iface_desc = serial->interface->cur_altsetting; ++ ++ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { ++ endpoint = &iface_desc->endpoint[i].desc; ++ if (usb_endpoint_is_bulk_in(endpoint)) ++ ++num_bulk_in; ++ if (usb_endpoint_is_bulk_out(endpoint)) ++ ++num_bulk_out; ++ } ++ ++ min_num_bulk = COMMAND_PORT + 1; ++ if (num_bulk_in < min_num_bulk || num_bulk_out < min_num_bulk) ++ return -ENODEV; ++ ++ return 0; ++} ++ + static int whiteheat_attach(struct usb_serial *serial) + { + struct usb_serial_port *command_port; +-- +2.4.3 + diff --git a/acpi-video-Add-force-native-backlight-quirk-for-Leno.patch b/acpi-video-Add-force-native-backlight-quirk-for-Leno.patch new file mode 100644 index 000000000..706fc540d --- /dev/null +++ b/acpi-video-Add-force-native-backlight-quirk-for-Leno.patch @@ -0,0 +1,81 @@ +From: Hans de Goede +Date: Tue, 3 Mar 2015 08:31:24 +0100 +Subject: [PATCH] acpi: video: Add force native backlight quirk for Lenovo + Ideapad Z570 + +The Lenovo Ideapad Z570 (which is an Acer in disguise like some other Ideapads) +has a broken acpi_video interface, this was fixed in commmit a11d342fb8 +("ACPI / video: force vendor backlight on Lenovo Ideapad Z570"). + +Which stops acpi_video from registering a backlight interface, but this is +only a partial fix, because for people who have the ideapad-laptop module +installed that module will now register a backlight interface, which also +does not work, so we need to use the native intel_backlight interface. + +The Lenovo Ideapad 570 is a pre-win8 laptop / too old for the acpi-video code +to automatically prefer the native backlight interface, so add a quirk for it. + +This commit also removes the previous incomplete fix. + +BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1187004 +Cc: Stepan Bujnak +Signed-off-by: Hans de Goede +--- + drivers/acpi/video.c | 17 +++++++++++++++++ + drivers/acpi/video_detect.c | 8 -------- + 2 files changed, 17 insertions(+), 8 deletions(-) + +diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c +index 70ea37bea84f..d9bf8ba7d848 100644 +--- a/drivers/acpi/video.c ++++ b/drivers/acpi/video.c +@@ -425,6 +425,12 @@ static int __init video_disable_native_backlight(const struct dmi_system_id *d) + return 0; + } + ++static int __init video_enable_native_backlight(const struct dmi_system_id *d) ++{ ++ use_native_backlight_dmi = NATIVE_BACKLIGHT_ON; ++ return 0; ++} ++ + static struct dmi_system_id video_dmi_table[] __initdata = { + /* + * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 +@@ -566,6 +572,17 @@ static struct dmi_system_id video_dmi_table[] __initdata = { + DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), + }, + }, ++ ++ /* Non win8 machines which need native backlight nevertheless */ ++ { ++ /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ ++ .callback = video_enable_native_backlight, ++ .ident = "Lenovo Ideapad Z570", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), ++ }, ++ }, + {} + }; + +diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c +index 27c43499977a..c42feb2bacd0 100644 +--- a/drivers/acpi/video_detect.c ++++ b/drivers/acpi/video_detect.c +@@ -174,14 +174,6 @@ static struct dmi_system_id video_detect_dmi_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"), + }, + }, +- { +- .callback = video_detect_force_vendor, +- .ident = "Lenovo IdeaPad Z570", +- .matches = { +- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +- DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"), +- }, +- }, + { }, + }; + diff --git a/acpi-video-Allow-forcing-native-backlight-on-non-win.patch b/acpi-video-Allow-forcing-native-backlight-on-non-win.patch new file mode 100644 index 000000000..2e6627be3 --- /dev/null +++ b/acpi-video-Allow-forcing-native-backlight-on-non-win.patch @@ -0,0 +1,73 @@ +From: Aaron Lu +Date: Wed, 11 Mar 2015 14:14:56 +0800 +Subject: [PATCH] acpi: video: Allow forcing native backlight on non win8 + machines + +The native backlight behavior (so not registering both the acpi-video +and the vendor backlight driver) can be useful on some non win8 machines +too, so change the behavior of the video.use_native_backlight=1 or 0 +kernel cmdline option to be: if user has set video.use_native_backlight=1 +or 0, use that no matter if it is a win8 system or not. Also, we will +put some known systems into the DMI table to make them either use native +backlight interface or not, and the use_native_backlight_dmi is used to +reflect that. + +Original-by: Hans de Goede +Signed-off-by: Aaron Lu +Acked-by: Hans de Goede +Signed-off-by: Hans de Goede +--- + drivers/acpi/video.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c +index db70d550f526..70ea37bea84f 100644 +--- a/drivers/acpi/video.c ++++ b/drivers/acpi/video.c +@@ -82,9 +82,15 @@ module_param(allow_duplicates, bool, 0644); + * For Windows 8 systems: used to decide if video module + * should skip registering backlight interface of its own. + */ +-static int use_native_backlight_param = -1; ++enum { ++ NATIVE_BACKLIGHT_NOT_SET = -1, ++ NATIVE_BACKLIGHT_OFF, ++ NATIVE_BACKLIGHT_ON, ++}; ++ ++static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET; + module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); +-static bool use_native_backlight_dmi = true; ++static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET; + + static int register_count; + static struct mutex video_list_lock; +@@ -237,15 +243,16 @@ static void acpi_video_switch_brightness(struct work_struct *work); + + static bool acpi_video_use_native_backlight(void) + { +- if (use_native_backlight_param != -1) ++ if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET) + return use_native_backlight_param; +- else ++ else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET) + return use_native_backlight_dmi; ++ return acpi_osi_is_win8(); + } + + bool acpi_video_verify_backlight_support(void) + { +- if (acpi_osi_is_win8() && acpi_video_use_native_backlight() && ++ if (acpi_video_use_native_backlight() && + backlight_device_registered(BACKLIGHT_RAW)) + return false; + return acpi_video_backlight_support(); +@@ -414,7 +421,7 @@ static int __init video_set_bqc_offset(const struct dmi_system_id *d) + + static int __init video_disable_native_backlight(const struct dmi_system_id *d) + { +- use_native_backlight_dmi = false; ++ use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF; + return 0; + } + diff --git a/antenna_select.patch b/antenna_select.patch new file mode 100644 index 000000000..15763e9bc --- /dev/null +++ b/antenna_select.patch @@ -0,0 +1,227 @@ +From c18d8f5095715c56bb3cd9cba64242542632054b Mon Sep 17 00:00:00 2001 +From: Larry Finger +Date: Wed, 16 Mar 2016 13:33:34 -0500 +Subject: rtlwifi: rtl8723be: Add antenna select module parameter + +A number of new laptops have been delivered with only a single antenna. +In principle, this is OK; however, a problem arises when the on-board +EEPROM is programmed to use the other antenna connection. The option +of opening the computer and moving the connector is not always possible +as it will void the warranty in some cases. In addition, this solution +breaks the Windows driver when the box dual boots Linux and Windows. + +A fix involving a new module parameter has been developed. This commit +adds the new parameter and implements the changes needed for the driver. + +Signed-off-by: Larry Finger +Cc: Stable [V4.0+] +Signed-off-by: Kalle Valo +--- + drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c | 5 +++++ + drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c | 3 +++ + drivers/net/wireless/realtek/rtlwifi/wifi.h | 3 +++ + 3 files changed, 11 insertions(+) + +diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c +index c983d2f..5a3df91 100644 +--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c ++++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c +@@ -2684,6 +2684,7 @@ void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, + bool auto_load_fail, u8 *hwinfo) + { + struct rtl_priv *rtlpriv = rtl_priv(hw); ++ struct rtl_mod_params *mod_params = rtlpriv->cfg->mod_params; + u8 value; + u32 tmpu_32; + +@@ -2702,6 +2703,10 @@ void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, + rtlpriv->btcoexist.btc_info.ant_num = ANT_X2; + } + ++ /* override ant_num / ant_path */ ++ if (mod_params->ant_sel) ++ rtlpriv->btcoexist.btc_info.ant_num = ++ (mod_params->ant_sel == 1 ? ANT_X2 : ANT_X1); + } + + void rtl8723be_bt_reg_init(struct ieee80211_hw *hw) +diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +index a78eaed..2101793 100644 +--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c ++++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +@@ -273,6 +273,7 @@ static struct rtl_mod_params rtl8723be_mod_params = { + .msi_support = false, + .disable_watchdog = false, + .debug = DBG_EMERG, ++ .ant_sel = 0, + }; + + static struct rtl_hal_cfg rtl8723be_hal_cfg = { +@@ -394,6 +395,7 @@ module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444); + module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444); + module_param_named(disable_watchdog, rtl8723be_mod_params.disable_watchdog, + bool, 0444); ++module_param_named(ant_sel, rtl8723be_mod_params.ant_sel, int, 0444); + MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); + MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); + MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); +@@ -402,6 +404,7 @@ MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); + MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); + MODULE_PARM_DESC(disable_watchdog, + "Set to 1 to disable the watchdog (default 0)\n"); ++MODULE_PARM_DESC(ant_sel, "Set to 1 or 2 to force antenna number (default 0)\n"); + + static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); + +diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h +index 554d814..93bd7fc 100644 +--- a/drivers/net/wireless/realtek/rtlwifi/wifi.h ++++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h +@@ -2246,6 +2246,9 @@ struct rtl_mod_params { + + /* default 0: 1 means do not disable interrupts */ + bool int_clear; ++ ++ /* select antenna */ ++ int ant_sel; + }; + + struct rtl_hal_usbint_cfg { +-- +cgit v0.12 + +From baa1702290953295e421f0f433e2b1ff4815827c Mon Sep 17 00:00:00 2001 +From: Larry Finger +Date: Wed, 16 Mar 2016 13:33:35 -0500 +Subject: rtlwifi: btcoexist: Implement antenna selection + +The previous patch added an option to rtl8723be to manually select the +antenna for those cases when only a single antenna is present, and the +on-board EEPROM is incorrectly programmed. This patch implements the +necessary changes in the Bluetooth coexistence driver. + +Signed-off-by: Larry Finger +Cc: Stable [V4.0+] +Signed-off-by: Kalle Valo +--- + .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 9 ++++++-- + .../realtek/rtlwifi/btcoexist/halbtcoutsrc.c | 27 +++++++++++++++++++++- + .../realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 2 +- + .../wireless/realtek/rtlwifi/btcoexist/rtl_btc.c | 5 +++- + 4 files changed, 38 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +index c43ab59..77cbd10 100644 +--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c ++++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +@@ -1203,7 +1203,6 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, + + /* Force GNT_BT to low */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x0); +- btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); + + if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { + /* tell firmware "no antenna inverse" */ +@@ -1211,19 +1210,25 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, + h2c_parameter[1] = 1; /* ext switch type */ + btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, + h2c_parameter); ++ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); + } else { + /* tell firmware "antenna inverse" */ + h2c_parameter[0] = 1; + h2c_parameter[1] = 1; /* ext switch type */ + btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, + h2c_parameter); ++ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); + } + } + + /* ext switch setting */ + if (use_ext_switch) { + /* fixed internal switch S1->WiFi, S0->BT */ +- btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); ++ if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) ++ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); ++ else ++ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); ++ + switch (antpos_type) { + case BTC_ANT_WIFI_AT_MAIN: + /* ext switch main at wifi */ +diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c +index b2791c8..babd149 100644 +--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c ++++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c +@@ -965,13 +965,38 @@ void exhalbtc_set_chip_type(u8 chip_type) + } + } + +-void exhalbtc_set_ant_num(u8 type, u8 ant_num) ++void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num) + { + if (BT_COEX_ANT_TYPE_PG == type) { + gl_bt_coexist.board_info.pg_ant_num = ant_num; + gl_bt_coexist.board_info.btdm_ant_num = ant_num; ++ /* The antenna position: ++ * Main (default) or Aux for pgAntNum=2 && btdmAntNum =1. ++ * The antenna position should be determined by ++ * auto-detect mechanism. ++ * The following is assumed to main, ++ * and those must be modified ++ * if y auto-detect mechanism is ready ++ */ ++ if ((gl_bt_coexist.board_info.pg_ant_num == 2) && ++ (gl_bt_coexist.board_info.btdm_ant_num == 1)) ++ gl_bt_coexist.board_info.btdm_ant_pos = ++ BTC_ANTENNA_AT_MAIN_PORT; ++ else ++ gl_bt_coexist.board_info.btdm_ant_pos = ++ BTC_ANTENNA_AT_MAIN_PORT; + } else if (BT_COEX_ANT_TYPE_ANTDIV == type) { + gl_bt_coexist.board_info.btdm_ant_num = ant_num; ++ gl_bt_coexist.board_info.btdm_ant_pos = ++ BTC_ANTENNA_AT_MAIN_PORT; ++ } else if (type == BT_COEX_ANT_TYPE_DETECTED) { ++ gl_bt_coexist.board_info.btdm_ant_num = ant_num; ++ if (rtlpriv->cfg->mod_params->ant_sel == 1) ++ gl_bt_coexist.board_info.btdm_ant_pos = ++ BTC_ANTENNA_AT_AUX_PORT; ++ else ++ gl_bt_coexist.board_info.btdm_ant_pos = ++ BTC_ANTENNA_AT_MAIN_PORT; + } + } + +diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +index 0a903ea..f41ca57 100644 +--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h ++++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +@@ -535,7 +535,7 @@ void exhalbtc_set_bt_patch_version(u16 bt_hci_version, u16 bt_patch_version); + void exhalbtc_update_min_bt_rssi(char bt_rssi); + void exhalbtc_set_bt_exist(bool bt_exist); + void exhalbtc_set_chip_type(u8 chip_type); +-void exhalbtc_set_ant_num(u8 type, u8 ant_num); ++void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num); + void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist); + void exhalbtc_signal_compensation(struct btc_coexist *btcoexist, + u8 *rssi_wifi, u8 *rssi_bt); +diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c +index b9b0cb7..d3fd921 100644 +--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c ++++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c +@@ -72,7 +72,10 @@ void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv) + __func__, bt_type); + exhalbtc_set_chip_type(bt_type); + +- exhalbtc_set_ant_num(BT_COEX_ANT_TYPE_PG, ant_num); ++ if (rtlpriv->cfg->mod_params->ant_sel == 1) ++ exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_DETECTED, 1); ++ else ++ exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_PG, ant_num); + } + + void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv) +-- +cgit v0.12 + diff --git a/arcmsr-buffer-overflow-in-archmsr_iop_message_xfer.patch b/arcmsr-buffer-overflow-in-archmsr_iop_message_xfer.patch deleted file mode 100644 index 81ed8814d..000000000 --- a/arcmsr-buffer-overflow-in-archmsr_iop_message_xfer.patch +++ /dev/null @@ -1,41 +0,0 @@ -From: Dan Carpenter -Date: 2016-09-15 13:44:56 -Subject: [patch v2] arcmsr: buffer overflow in arcmsr_iop_message_xfer() - -We need to put an upper bound on "user_len" so the memcpy() doesn't -overflow. - -Reported-by: Marco Grassi -Signed-off-by: Dan Carpenter -Reviewed-by: Tomas Henzl - -diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c -index 7640498..110eca9 100644 ---- a/drivers/scsi/arcmsr/arcmsr_hba.c -+++ b/drivers/scsi/arcmsr/arcmsr_hba.c -@@ -2388,7 +2388,8 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, - } - case ARCMSR_MESSAGE_WRITE_WQBUFFER: { - unsigned char *ver_addr; -- int32_t user_len, cnt2end; -+ uint32_t user_len; -+ int32_t cnt2end; - uint8_t *pQbuffer, *ptmpuserbuffer; - ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC); - if (!ver_addr) { -@@ -2397,6 +2398,11 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, - } - ptmpuserbuffer = ver_addr; - user_len = pcmdmessagefld->cmdmessage.Length; -+ if (user_len > ARCMSR_API_DATA_BUFLEN) { -+ retvalue = ARCMSR_MESSAGE_FAIL; -+ kfree(ver_addr); -+ goto message_out; -+ } - memcpy(ptmpuserbuffer, - pcmdmessagefld->messagedatabuffer, user_len); - spin_lock_irqsave(&acb->wqbuffer_lock, flags); --- -To unsubscribe from this list: send the line "unsubscribe linux-scsi" in -the body of a message to majordomo@vger.kernel.org -More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/arm-revert-mmc-omap_hsmmc-Use-dma_request_chan-for-reque.patch b/arm-revert-mmc-omap_hsmmc-Use-dma_request_chan-for-reque.patch new file mode 100644 index 000000000..b55dec0cb --- /dev/null +++ b/arm-revert-mmc-omap_hsmmc-Use-dma_request_chan-for-reque.patch @@ -0,0 +1,100 @@ +From bb3e08008c0e48fd4f51a0f0957eecae61a24d69 Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 1 Nov 2016 09:35:30 +0000 +Subject: [PATCH] Revert "mmc: omap_hsmmc: Use dma_request_chan() for + requesting DMA channel" + +This reverts commit 81eef6ca92014845d40e3f1310e42b7010303acc. +--- + drivers/mmc/host/omap_hsmmc.c | 50 ++++++++++++++++++++++++++++++++++--------- + 1 file changed, 40 insertions(+), 10 deletions(-) + +diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c +index 24ebc9a..3563321 100644 +--- a/drivers/mmc/host/omap_hsmmc.c ++++ b/drivers/mmc/host/omap_hsmmc.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1992,6 +1993,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev) + struct resource *res; + int ret, irq; + const struct of_device_id *match; ++ dma_cap_mask_t mask; ++ unsigned tx_req, rx_req; + const struct omap_mmc_of_data *data; + void __iomem *base; + +@@ -2121,17 +2124,44 @@ static int omap_hsmmc_probe(struct platform_device *pdev) + + omap_hsmmc_conf_bus_power(host); + +- host->rx_chan = dma_request_chan(&pdev->dev, "rx"); +- if (IS_ERR(host->rx_chan)) { +- dev_err(mmc_dev(host->mmc), "RX DMA channel request failed\n"); +- ret = PTR_ERR(host->rx_chan); ++ if (!pdev->dev.of_node) { ++ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); ++ if (!res) { ++ dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n"); ++ ret = -ENXIO; ++ goto err_irq; ++ } ++ tx_req = res->start; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); ++ if (!res) { ++ dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n"); ++ ret = -ENXIO; ++ goto err_irq; ++ } ++ rx_req = res->start; ++ } ++ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ ++ host->rx_chan = ++ dma_request_slave_channel_compat(mask, omap_dma_filter_fn, ++ &rx_req, &pdev->dev, "rx"); ++ ++ if (!host->rx_chan) { ++ dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n"); ++ ret = -ENXIO; + goto err_irq; + } + +- host->tx_chan = dma_request_chan(&pdev->dev, "tx"); +- if (IS_ERR(host->tx_chan)) { +- dev_err(mmc_dev(host->mmc), "TX DMA channel request failed\n"); +- ret = PTR_ERR(host->tx_chan); ++ host->tx_chan = ++ dma_request_slave_channel_compat(mask, omap_dma_filter_fn, ++ &tx_req, &pdev->dev, "tx"); ++ ++ if (!host->tx_chan) { ++ dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n"); ++ ret = -ENXIO; + goto err_irq; + } + +@@ -2189,9 +2219,9 @@ err_slot_name: + mmc_remove_host(mmc); + err_irq: + device_init_wakeup(&pdev->dev, false); +- if (!IS_ERR_OR_NULL(host->tx_chan)) ++ if (host->tx_chan) + dma_release_channel(host->tx_chan); +- if (!IS_ERR_OR_NULL(host->rx_chan)) ++ if (host->rx_chan) + dma_release_channel(host->rx_chan); + pm_runtime_dont_use_autosuspend(host->dev); + pm_runtime_put_sync(host->dev); +-- +2.9.3 + diff --git a/arm64-mm-Fix-memmap-to-be-initialized-for-the-entire-section.patch b/arm64-mm-Fix-memmap-to-be-initialized-for-the-entire-section.patch new file mode 100644 index 000000000..eaf809d53 --- /dev/null +++ b/arm64-mm-Fix-memmap-to-be-initialized-for-the-entire-section.patch @@ -0,0 +1,93 @@ +From patchwork Thu Oct 6 09:52:07 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: arm64: mm: Fix memmap to be initialized for the entire section +From: Robert Richter +X-Patchwork-Id: 9364537 +Message-Id: <1475747527-32387-1-git-send-email-rrichter@cavium.com> +To: Catalin Marinas , Will Deacon + +Cc: Mark Rutland , linux-efi@vger.kernel.org, + David Daney , + Ard Biesheuvel , + linux-kernel@vger.kernel.org, Robert Richter , + Hanjun Guo , linux-arm-kernel@lists.infradead.org +Date: Thu, 6 Oct 2016 11:52:07 +0200 + +There is a memory setup problem on ThunderX systems with certain +memory configurations. The symptom is + + kernel BUG at mm/page_alloc.c:1848! + +This happens for some configs with 64k page size enabled. The bug +triggers for page zones with some pages in the zone not assigned to +this particular zone. In my case some pages that are marked as nomap +were not reassigned to the new zone of node 1, so those are still +assigned to node 0. + +The reason for the mis-configuration is a change in pfn_valid() which +reports pages marked nomap as invalid: + + 68709f45385a arm64: only consider memblocks with NOMAP cleared for linear mapping + +This causes pages marked as nomap being no long reassigned to the new +zone in memmap_init_zone() by calling __init_single_pfn(). + +Fixing this by restoring the old behavior of pfn_valid() to use +memblock_is_memory(). Also changing users of pfn_valid() in arm64 code +to use memblock_is_map_memory() where necessary. This only affects +code in ioremap.c. The code in mmu.c still can use the new version of +pfn_valid(). + +Should be marked stable v4.5.. + +Signed-off-by: Robert Richter +--- + arch/arm64/mm/init.c | 2 +- + arch/arm64/mm/ioremap.c | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c +index bbb7ee76e319..25b8659c2a9f 100644 +--- a/arch/arm64/mm/init.c ++++ b/arch/arm64/mm/init.c +@@ -147,7 +147,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max) + #ifdef CONFIG_HAVE_ARCH_PFN_VALID + int pfn_valid(unsigned long pfn) + { +- return memblock_is_map_memory(pfn << PAGE_SHIFT); ++ return memblock_is_memory(pfn << PAGE_SHIFT); + } + EXPORT_SYMBOL(pfn_valid); + #endif +diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c +index 01e88c8bcab0..c17c220b0c48 100644 +--- a/arch/arm64/mm/ioremap.c ++++ b/arch/arm64/mm/ioremap.c +@@ -21,6 +21,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -55,7 +56,7 @@ static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size, + /* + * Don't allow RAM to be mapped. + */ +- if (WARN_ON(pfn_valid(__phys_to_pfn(phys_addr)))) ++ if (WARN_ON(memblock_is_map_memory(phys_addr))) + return NULL; + + area = get_vm_area_caller(size, VM_IOREMAP, caller); +@@ -96,7 +97,7 @@ EXPORT_SYMBOL(__iounmap); + void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size) + { + /* For normal memory we already have a cacheable mapping. */ +- if (pfn_valid(__phys_to_pfn(phys_addr))) ++ if (memblock_is_map_memory(phys_addr)) + return (void __iomem *)__phys_to_virt(phys_addr); + + return __ioremap_caller(phys_addr, size, __pgprot(PROT_NORMAL), diff --git a/arm64-pcie-acpi.patch b/arm64-pcie-acpi.patch new file mode 100644 index 000000000..e9a359db6 --- /dev/null +++ b/arm64-pcie-acpi.patch @@ -0,0 +1,1247 @@ +From 1fc02559de87cd88339a83ad05baa9c2b5bd1ac0 Mon Sep 17 00:00:00 2001 +From: Jayachandran C +Date: Fri, 10 Jun 2016 21:55:09 +0200 +Subject: [PATCH 01/11] PCI/ECAM: Move ecam.h to linux/include/pci-ecam.h + +This header will be used from arch/arm64 for ACPI PCI implementation +so it needs to be moved out of drivers/pci. + +Update users of the header file to use the new name. No functional +changes. + +Signed-off-by: Jayachandran C +Acked-by: Lorenzo Pieralisi +--- + drivers/pci/ecam.c | 3 +- + drivers/pci/ecam.h | 67 ------------------------------------- + drivers/pci/host/pci-host-common.c | 3 +- + drivers/pci/host/pci-host-generic.c | 3 +- + drivers/pci/host/pci-thunder-ecam.c | 3 +- + drivers/pci/host/pci-thunder-pem.c | 3 +- + include/linux/pci-ecam.h | 67 +++++++++++++++++++++++++++++++++++++ + 7 files changed, 72 insertions(+), 77 deletions(-) + delete mode 100644 drivers/pci/ecam.h + create mode 100644 include/linux/pci-ecam.h + +diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c +index f9832ad..820e26b 100644 +--- a/drivers/pci/ecam.c ++++ b/drivers/pci/ecam.c +@@ -19,10 +19,9 @@ + #include + #include + #include ++#include + #include + +-#include "ecam.h" +- + /* + * On 64-bit systems, we do a single ioremap for the whole config space + * since we have enough virtual address range available. On 32-bit, we +diff --git a/drivers/pci/ecam.h b/drivers/pci/ecam.h +deleted file mode 100644 +index 9878beb..0000000 +--- a/drivers/pci/ecam.h ++++ /dev/null +@@ -1,67 +0,0 @@ +-/* +- * Copyright 2016 Broadcom +- * +- * 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 (the "GPL"). +- * +- * 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 version 2 (GPLv2) for more details. +- * +- * You should have received a copy of the GNU General Public License +- * version 2 (GPLv2) along with this source code. +- */ +-#ifndef DRIVERS_PCI_ECAM_H +-#define DRIVERS_PCI_ECAM_H +- +-#include +-#include +- +-/* +- * struct to hold pci ops and bus shift of the config window +- * for a PCI controller. +- */ +-struct pci_config_window; +-struct pci_ecam_ops { +- unsigned int bus_shift; +- struct pci_ops pci_ops; +- int (*init)(struct device *, +- struct pci_config_window *); +-}; +- +-/* +- * struct to hold the mappings of a config space window. This +- * is expected to be used as sysdata for PCI controllers that +- * use ECAM. +- */ +-struct pci_config_window { +- struct resource res; +- struct resource busr; +- void *priv; +- struct pci_ecam_ops *ops; +- union { +- void __iomem *win; /* 64-bit single mapping */ +- void __iomem **winp; /* 32-bit per-bus mapping */ +- }; +-}; +- +-/* create and free pci_config_window */ +-struct pci_config_window *pci_ecam_create(struct device *dev, +- struct resource *cfgres, struct resource *busr, +- struct pci_ecam_ops *ops); +-void pci_ecam_free(struct pci_config_window *cfg); +- +-/* map_bus when ->sysdata is an instance of pci_config_window */ +-void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, +- int where); +-/* default ECAM ops */ +-extern struct pci_ecam_ops pci_generic_ecam_ops; +- +-#ifdef CONFIG_PCI_HOST_GENERIC +-/* for DT-based PCI controllers that support ECAM */ +-int pci_host_common_probe(struct platform_device *pdev, +- struct pci_ecam_ops *ops); +-#endif +-#endif +diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c +index 8cba7ab..c18b9e3 100644 +--- a/drivers/pci/host/pci-host-common.c ++++ b/drivers/pci/host/pci-host-common.c +@@ -20,10 +20,9 @@ + #include + #include + #include ++#include + #include + +-#include "../ecam.h" +- + static int gen_pci_parse_request_of_pci_ranges(struct device *dev, + struct list_head *resources, struct resource **bus_range) + { +diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c +index 6eaceab..f0ca6de 100644 +--- a/drivers/pci/host/pci-host-generic.c ++++ b/drivers/pci/host/pci-host-generic.c +@@ -23,10 +23,9 @@ + #include + #include + #include ++#include + #include + +-#include "../ecam.h" +- + static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { + .bus_shift = 16, + .pci_ops = { +diff --git a/drivers/pci/host/pci-thunder-ecam.c b/drivers/pci/host/pci-thunder-ecam.c +index 540d030..a9fc1c9 100644 +--- a/drivers/pci/host/pci-thunder-ecam.c ++++ b/drivers/pci/host/pci-thunder-ecam.c +@@ -11,10 +11,9 @@ + #include + #include + #include ++#include + #include + +-#include "../ecam.h" +- + static void set_val(u32 v, int where, int size, u32 *val) + { + int shift = (where & 3) * 8; +diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c +index 9b8ab94..5020d3d 100644 +--- a/drivers/pci/host/pci-thunder-pem.c ++++ b/drivers/pci/host/pci-thunder-pem.c +@@ -18,10 +18,9 @@ + #include + #include + #include ++#include + #include + +-#include "../ecam.h" +- + #define PEM_CFG_WR 0x28 + #define PEM_CFG_RD 0x30 + +diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h +new file mode 100644 +index 0000000..9878beb +--- /dev/null ++++ b/include/linux/pci-ecam.h +@@ -0,0 +1,67 @@ ++/* ++ * Copyright 2016 Broadcom ++ * ++ * 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 (the "GPL"). ++ * ++ * 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 version 2 (GPLv2) for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2 (GPLv2) along with this source code. ++ */ ++#ifndef DRIVERS_PCI_ECAM_H ++#define DRIVERS_PCI_ECAM_H ++ ++#include ++#include ++ ++/* ++ * struct to hold pci ops and bus shift of the config window ++ * for a PCI controller. ++ */ ++struct pci_config_window; ++struct pci_ecam_ops { ++ unsigned int bus_shift; ++ struct pci_ops pci_ops; ++ int (*init)(struct device *, ++ struct pci_config_window *); ++}; ++ ++/* ++ * struct to hold the mappings of a config space window. This ++ * is expected to be used as sysdata for PCI controllers that ++ * use ECAM. ++ */ ++struct pci_config_window { ++ struct resource res; ++ struct resource busr; ++ void *priv; ++ struct pci_ecam_ops *ops; ++ union { ++ void __iomem *win; /* 64-bit single mapping */ ++ void __iomem **winp; /* 32-bit per-bus mapping */ ++ }; ++}; ++ ++/* create and free pci_config_window */ ++struct pci_config_window *pci_ecam_create(struct device *dev, ++ struct resource *cfgres, struct resource *busr, ++ struct pci_ecam_ops *ops); ++void pci_ecam_free(struct pci_config_window *cfg); ++ ++/* map_bus when ->sysdata is an instance of pci_config_window */ ++void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, ++ int where); ++/* default ECAM ops */ ++extern struct pci_ecam_ops pci_generic_ecam_ops; ++ ++#ifdef CONFIG_PCI_HOST_GENERIC ++/* for DT-based PCI controllers that support ECAM */ ++int pci_host_common_probe(struct platform_device *pdev, ++ struct pci_ecam_ops *ops); ++#endif ++#endif +-- +2.7.4 + +From 5eb9996fc097629854f359f9ad3d959fdacb7f8f Mon Sep 17 00:00:00 2001 +From: Jayachandran C +Date: Fri, 10 Jun 2016 21:55:10 +0200 +Subject: [PATCH 02/11] PCI/ECAM: Add parent device field to pci_config_window + +Add a parent device field to struct pci_config_window. The parent +is not saved now, but will be useful to save it in some cases. +Specifically in case of ACPI for ARM64, it can be used to setup +ACPI companion and domain. + +Since the parent dev is in struct pci_config_window now, we need +not pass it to the init function as a separate argument. + +Signed-off-by: Jayachandran C +Acked-by: Lorenzo Pieralisi +--- + drivers/pci/ecam.c | 3 ++- + drivers/pci/host/pci-thunder-pem.c | 3 ++- + include/linux/pci-ecam.h | 4 ++-- + 3 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c +index 820e26b..66e0d71 100644 +--- a/drivers/pci/ecam.c ++++ b/drivers/pci/ecam.c +@@ -51,6 +51,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev, + if (!cfg) + return ERR_PTR(-ENOMEM); + ++ cfg->parent = dev; + cfg->ops = ops; + cfg->busr.start = busr->start; + cfg->busr.end = busr->end; +@@ -94,7 +95,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev, + } + + if (ops->init) { +- err = ops->init(dev, cfg); ++ err = ops->init(cfg); + if (err) + goto err_exit; + } +diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c +index 5020d3d..91f6fc6 100644 +--- a/drivers/pci/host/pci-thunder-pem.c ++++ b/drivers/pci/host/pci-thunder-pem.c +@@ -284,8 +284,9 @@ static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn, + return pci_generic_config_write(bus, devfn, where, size, val); + } + +-static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg) ++static int thunder_pem_init(struct pci_config_window *cfg) + { ++ struct device *dev = cfg->parent; + resource_size_t bar4_start; + struct resource *res_pem; + struct thunder_pem_pci *pem_pci; +diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h +index 9878beb..7adad20 100644 +--- a/include/linux/pci-ecam.h ++++ b/include/linux/pci-ecam.h +@@ -27,8 +27,7 @@ struct pci_config_window; + struct pci_ecam_ops { + unsigned int bus_shift; + struct pci_ops pci_ops; +- int (*init)(struct device *, +- struct pci_config_window *); ++ int (*init)(struct pci_config_window *); + }; + + /* +@@ -45,6 +44,7 @@ struct pci_config_window { + void __iomem *win; /* 64-bit single mapping */ + void __iomem **winp; /* 32-bit per-bus mapping */ + }; ++ struct device *parent;/* ECAM res was from this dev */ + }; + + /* create and free pci_config_window */ +-- +2.7.4 + +From 6ed6c1365df5c9201e9b275e8ed4eaa64ef2ec0d Mon Sep 17 00:00:00 2001 +From: Sinan Kaya +Date: Fri, 10 Jun 2016 21:55:11 +0200 +Subject: [PATCH 03/11] PCI: Add new function to unmap IO resources + +We need to release I/O resources so that the same I/O resources +can be allocated again in pci_remap_iospace(), like in PCI hotplug removal +scenario. Therefore implement new pci_unmap_iospace() call which +unmaps I/O space as the symmetry to pci_remap_iospace(). + +Signed-off-by: Sinan Kaya +Signed-off-by: Tomasz Nowicki +Acked-by: Lorenzo Pieralisi +--- + drivers/pci/pci.c | 18 ++++++++++++++++++ + include/linux/pci.h | 1 + + 2 files changed, 19 insertions(+) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index c8b4dbd..eb431b5 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include "pci.h" +@@ -3165,6 +3166,23 @@ int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr) + #endif + } + ++/** ++ * pci_unmap_iospace - Unmap the memory mapped I/O space ++ * @res: resource to be unmapped ++ * ++ * Unmap the CPU virtual address @res from virtual address space. ++ * Only architectures that have memory mapped IO functions defined ++ * (and the PCI_IOBASE value defined) should call this function. ++ */ ++void pci_unmap_iospace(struct resource *res) ++{ ++#if defined(PCI_IOBASE) && defined(CONFIG_MMU) ++ unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start; ++ ++ unmap_kernel_range(vaddr, resource_size(res)); ++#endif ++} ++ + static void __pci_set_master(struct pci_dev *dev, bool enable) + { + u16 old_cmd, cmd; +diff --git a/include/linux/pci.h b/include/linux/pci.h +index b67e4df..12349de 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1167,6 +1167,7 @@ int pci_register_io_range(phys_addr_t addr, resource_size_t size); + unsigned long pci_address_to_pio(phys_addr_t addr); + phys_addr_t pci_pio_to_address(unsigned long pio); + int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr); ++void pci_unmap_iospace(struct resource *res); + + static inline pci_bus_addr_t pci_bus_address(struct pci_dev *pdev, int bar) + { +-- +2.7.4 + +From 62f4d54fc2d2882b9ebaa920189574f422e12207 Mon Sep 17 00:00:00 2001 +From: Jayachandran C +Date: Fri, 10 Jun 2016 21:55:12 +0200 +Subject: [PATCH 04/11] ACPI/PCI: Support IO resources when parsing PCI host + bridge resources + +Platforms that have memory mapped IO port (such as ARM64) need special +handling for PCI I/O resources. For host bridge's resource probing case +these resources need to be fixed up with +pci_register_io_range()/pci_remap_iospace() etc. + +The same I/O resources need to be released after hotplug +removal so that it can be re-added back by the pci_remap_iospace() +function during insertion. As a consequence unmap I/O resources +with pci_unmap_iospace() when we release host bridge resources. + +Signed-off-by: Jayachandran C +Signed-off-by: Sinan Kaya +[ Tomasz: merged in Sinan's patch to unmap IO resources properly, updated changelog] +Signed-off-by: Tomasz Nowicki +Reviewed-by: Lorenzo Pieralisi +--- + drivers/acpi/pci_root.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c +index ae3fe4e..9a26dd1 100644 +--- a/drivers/acpi/pci_root.c ++++ b/drivers/acpi/pci_root.c +@@ -720,6 +720,40 @@ next: + } + } + ++#ifdef PCI_IOBASE ++static void acpi_pci_root_remap_iospace(struct resource_entry *entry) ++{ ++ struct resource *res = entry->res; ++ resource_size_t cpu_addr = res->start; ++ resource_size_t pci_addr = cpu_addr - entry->offset; ++ resource_size_t length = resource_size(res); ++ unsigned long port; ++ ++ if (pci_register_io_range(cpu_addr, length)) ++ goto err; ++ ++ port = pci_address_to_pio(cpu_addr); ++ if (port == (unsigned long)-1) ++ goto err; ++ ++ res->start = port; ++ res->end = port + length - 1; ++ entry->offset = port - pci_addr; ++ ++ if (pci_remap_iospace(res, cpu_addr) < 0) ++ goto err; ++ ++ pr_info("Remapped I/O %pa to %pR\n", &cpu_addr, res); ++ return; ++err: ++ res->flags |= IORESOURCE_DISABLED; ++} ++#else ++static inline void acpi_pci_root_remap_iospace(struct resource_entry *entry) ++{ ++} ++#endif ++ + int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) + { + int ret; +@@ -740,6 +774,9 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) + "no IO and memory resources present in _CRS\n"); + else { + resource_list_for_each_entry_safe(entry, tmp, list) { ++ if (entry->res->flags & IORESOURCE_IO) ++ acpi_pci_root_remap_iospace(entry); ++ + if (entry->res->flags & IORESOURCE_DISABLED) + resource_list_destroy_entry(entry); + else +@@ -811,6 +848,8 @@ static void acpi_pci_root_release_info(struct pci_host_bridge *bridge) + + resource_list_for_each_entry(entry, &bridge->windows) { + res = entry->res; ++ if (res->flags & IORESOURCE_IO) ++ pci_unmap_iospace(res); + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); +-- +2.7.4 + +From dd4f7822d702cca83baa1de7a5f69344ffb82af9 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:13 +0200 +Subject: [PATCH 05/11] ACPI/PCI: Add generic MCFG table handling + +According to PCI firmware specifications, on systems booting with ACPI, +PCI configuration for a host bridge must be set-up through the MCFG table +regions for non-hotpluggable bridges and _CBA method for hotpluggable ones. + +Current MCFG table handling code, as implemented for x86, cannot be +easily generalized owing to x86 specific quirks handling and related +code, which makes it hard to reuse on other architectures. + +In order to implement MCFG PCI configuration handling for new platforms +booting with ACPI (eg ARM64) this patch re-implements MCFG handling from +scratch in a streamlined fashion and provides (through a generic +interface available to all arches): + +- Simplified MCFG table parsing (executed through the pci_mmcfg_late_init() + hook as in current x86) +- MCFG regions look-up interface through domain:bus_start:bus_end tuple + +The new MCFG regions handling interface is added to generic ACPI code +so that existing architectures (eg x86) can be moved over to it and +architectures relying on MCFG for ACPI PCI config space can rely on it +without having to resort to arch specific implementations. + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Jayachandran C +Reviewed-by: Lorenzo Pieralisi +--- + drivers/acpi/Kconfig | 3 ++ + drivers/acpi/Makefile | 1 + + drivers/acpi/pci_mcfg.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/pci-acpi.h | 2 ++ + include/linux/pci.h | 2 +- + 5 files changed, 99 insertions(+), 1 deletion(-) + create mode 100644 drivers/acpi/pci_mcfg.c + +diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig +index b7e2e77..f98c328 100644 +--- a/drivers/acpi/Kconfig ++++ b/drivers/acpi/Kconfig +@@ -217,6 +217,9 @@ config ACPI_PROCESSOR_IDLE + bool + select CPU_IDLE + ++config ACPI_MCFG ++ bool ++ + config ACPI_CPPC_LIB + bool + depends on ACPI_PROCESSOR +diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile +index 251ce85..632e81f 100644 +--- a/drivers/acpi/Makefile ++++ b/drivers/acpi/Makefile +@@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o + acpi-y += ec.o + acpi-$(CONFIG_ACPI_DOCK) += dock.o + acpi-y += pci_root.o pci_link.o pci_irq.o ++obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o + acpi-y += acpi_lpss.o acpi_apd.o + acpi-y += acpi_platform.o + acpi-y += acpi_pnp.o +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +new file mode 100644 +index 0000000..d3c3e85 +--- /dev/null ++++ b/drivers/acpi/pci_mcfg.c +@@ -0,0 +1,92 @@ ++/* ++ * Copyright (C) 2016 Broadcom ++ * Author: Jayachandran C ++ * Copyright (C) 2016 Semihalf ++ * Author: Tomasz Nowicki ++ * ++ * 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 (the "GPL"). ++ * ++ * 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 version 2 (GPLv2) for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * version 2 (GPLv2) along with this source code. ++ */ ++ ++#define pr_fmt(fmt) "ACPI: " fmt ++ ++#include ++#include ++#include ++ ++/* Structure to hold entries from the MCFG table */ ++struct mcfg_entry { ++ struct list_head list; ++ phys_addr_t addr; ++ u16 segment; ++ u8 bus_start; ++ u8 bus_end; ++}; ++ ++/* List to save mcfg entries */ ++static LIST_HEAD(pci_mcfg_list); ++ ++phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) ++{ ++ struct mcfg_entry *e; ++ ++ /* ++ * We expect exact match, unless MCFG entry end bus covers more than ++ * specified by caller. ++ */ ++ list_for_each_entry(e, &pci_mcfg_list, list) { ++ if (e->segment == seg && e->bus_start == bus_res->start && ++ e->bus_end >= bus_res->end) ++ return e->addr; ++ } ++ ++ return 0; ++} ++ ++static __init int pci_mcfg_parse(struct acpi_table_header *header) ++{ ++ struct acpi_table_mcfg *mcfg; ++ struct acpi_mcfg_allocation *mptr; ++ struct mcfg_entry *e, *arr; ++ int i, n; ++ ++ if (header->length < sizeof(struct acpi_table_mcfg)) ++ return -EINVAL; ++ ++ n = (header->length - sizeof(struct acpi_table_mcfg)) / ++ sizeof(struct acpi_mcfg_allocation); ++ mcfg = (struct acpi_table_mcfg *)header; ++ mptr = (struct acpi_mcfg_allocation *) &mcfg[1]; ++ ++ arr = kcalloc(n, sizeof(*arr), GFP_KERNEL); ++ if (!arr) ++ return -ENOMEM; ++ ++ for (i = 0, e = arr; i < n; i++, mptr++, e++) { ++ e->segment = mptr->pci_segment; ++ e->addr = mptr->address; ++ e->bus_start = mptr->start_bus_number; ++ e->bus_end = mptr->end_bus_number; ++ list_add(&e->list, &pci_mcfg_list); ++ } ++ ++ pr_info("MCFG table detected, %d entries\n", n); ++ return 0; ++} ++ ++/* Interface called by ACPI - parse and save MCFG table */ ++void __init pci_mmcfg_late_init(void) ++{ ++ int err = acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse); ++ if (err) ++ pr_err("Failed to parse MCFG (%d)\n", err); ++} +diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h +index 89ab057..7d63a66 100644 +--- a/include/linux/pci-acpi.h ++++ b/include/linux/pci-acpi.h +@@ -24,6 +24,8 @@ static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) + } + extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); + ++extern phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res); ++ + static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) + { + struct pci_bus *pbus = pdev->bus; +diff --git a/include/linux/pci.h b/include/linux/pci.h +index 12349de..ce03d65 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1723,7 +1723,7 @@ void pcibios_free_irq(struct pci_dev *dev); + extern struct dev_pm_ops pcibios_pm_ops; + #endif + +-#ifdef CONFIG_PCI_MMCONFIG ++#if defined(CONFIG_PCI_MMCONFIG) || defined(CONFIG_ACPI_MCFG) + void __init pci_mmcfg_early_init(void); + void __init pci_mmcfg_late_init(void); + #else +-- +2.7.4 + +From fc1907f9c79f9a0a0c2f4d579896e678915fec48 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:14 +0200 +Subject: [PATCH 06/11] PCI: Refactor generic bus domain assignment + +Change the way PCI bus domain number is assigned and improve function +name to reflect what function does. No functional changes. + +Instead of assigning bus domain number inside of pci_bus_assign_domain_nr() +simply return domain number and let pci_create_root_bus() do assignment. +This way pci_create_root_bus() setups bus structure data in the consistent +way. Since pci_bus_assign_domain_nr() now does not assign but retrieves +domain number instead, rename it to pci_bus_find_domain_nr(). + +Signed-off-by: Tomasz Nowicki +Reviewed-by: Lorenzo Pieralisi +--- + drivers/pci/pci.c | 4 ++-- + drivers/pci/probe.c | 4 +++- + include/linux/pci.h | 7 +------ + 3 files changed, 6 insertions(+), 9 deletions(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index eb431b5..b9a7833 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -4941,7 +4941,7 @@ int pci_get_new_domain_nr(void) + } + + #ifdef CONFIG_PCI_DOMAINS_GENERIC +-void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) ++int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) + { + static int use_dt_domains = -1; + int domain = -1; +@@ -4985,7 +4985,7 @@ void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) + domain = -1; + } + +- bus->domain_nr = domain; ++ return domain; + } + #endif + #endif +diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c +index 8e3ef72..380d46d 100644 +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -2127,7 +2127,9 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, + b->sysdata = sysdata; + b->ops = ops; + b->number = b->busn_res.start = bus; +- pci_bus_assign_domain_nr(b, parent); ++#ifdef CONFIG_PCI_DOMAINS_GENERIC ++ b->domain_nr = pci_bus_find_domain_nr(b, parent); ++#endif + b2 = pci_find_bus(pci_domain_nr(b), bus); + if (b2) { + /* If we already got to this bus through a different bridge, ignore it */ +diff --git a/include/linux/pci.h b/include/linux/pci.h +index ce03d65..48839e8 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1390,12 +1390,7 @@ static inline int pci_domain_nr(struct pci_bus *bus) + { + return bus->domain_nr; + } +-void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent); +-#else +-static inline void pci_bus_assign_domain_nr(struct pci_bus *bus, +- struct device *parent) +-{ +-} ++int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent); + #endif + + /* some architectures require additional setup to direct VGA traffic */ +-- +2.7.4 + +From d6d45ae1d58658111d5e838c41b9ed4729bbb81e Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:15 +0200 +Subject: [PATCH 07/11] PCI: Factor DT specific pci_bus_find_domain_nr() code + out + +pci_bus_find_domain_nr() retrieves the host bridge domain number in a DT +specific way. Factor our pci_bus_find_domain_nr() in a separate DT +function (ie of_pci_bus_find_domain_nr()) so that DT code is self +contained, paving the way for retrieving domain number in +pci_bus_find_domain_nr() with additional firmware methods (ie ACPI). + +Signed-off-by: Tomasz Nowicki +Reviewed-by: Lorenzo Pieralisi +--- + drivers/pci/pci.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index b9a7833..97f7cd4 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -4941,7 +4941,7 @@ int pci_get_new_domain_nr(void) + } + + #ifdef CONFIG_PCI_DOMAINS_GENERIC +-int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) ++static int of_pci_bus_find_domain_nr(struct device *parent) + { + static int use_dt_domains = -1; + int domain = -1; +@@ -4987,6 +4987,11 @@ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) + + return domain; + } ++ ++int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) ++{ ++ return of_pci_bus_find_domain_nr(parent); ++} + #endif + #endif + +-- +2.7.4 + +From 92d59511fd365d3c0c31d074b5e96cf48c58f68b Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Thu, 30 Jun 2016 16:56:24 +0100 +Subject: [PATCH 08/11] ARM64/PCI: Add ACPI hook to assign domain number + +PCI core code provides a config option (CONFIG_PCI_DOMAINS_GENERIC) +that allows assigning the PCI bus domain number generically by +relying on device tree bindings, and falling back to a simple counter +when the respective DT properties (ie "linux,pci-domain") are not +specified in the host bridge device tree node. + +In a similar way, when a system is booted through ACPI, architectures +that are selecting CONFIG_PCI_DOMAINS_GENERIC (ie ARM64) require kernel +hooks to retrieve the domain number so that the PCI bus domain number +set-up can be handled seamlessly with DT and ACPI in generic core code +when CONFIG_PCI_DOMAINS_GENERIC is selected. + +Since currently it is not possible to retrieve a pointer to the PCI +host bridge ACPI device backing the host bridge from core PCI code +(which would allow retrieving the domain number in an arch agnostic +way through the ACPI _SEG method), an arch specific ACPI hook has to +be declared and implemented by all arches that rely on +CONFIG_PCI_DOMAINS_GENERIC to retrieve the domain number and set it +up in core PCI code. + +For the aforementioned reasons, introduce acpi_pci_bus_find_domain_nr() +hook to retrieve the domain number on a per-arch basis when the system +boots through ACPI. ARM64 dummy implementation of the same is provided +in first place in preparation for ARM64 ACPI based PCI host controller +driver. + +acpi_pci_bus_find_domain_nr() is called from generic +pci_bus_find_domain_nr() as an ACPI option to DT domain assignment. + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Lorenzo Pieralisi +--- + arch/arm64/kernel/pci.c | 7 +++++++ + drivers/pci/pci.c | 4 +++- + include/linux/pci.h | 7 +++++++ + 3 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index 3c4e308..d5d3d26 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + + /* +@@ -85,6 +86,12 @@ EXPORT_SYMBOL(pcibus_to_node); + #endif + + #ifdef CONFIG_ACPI ++ ++int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) ++{ ++ return 0; ++} ++ + /* Root bridge scanning */ + struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) + { +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 97f7cd4..4834cee 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -7,6 +7,7 @@ + * Copyright 1997 -- 2000 Martin Mares + */ + ++#include + #include + #include + #include +@@ -4990,7 +4991,8 @@ static int of_pci_bus_find_domain_nr(struct device *parent) + + int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) + { +- return of_pci_bus_find_domain_nr(parent); ++ return acpi_disabled ? of_pci_bus_find_domain_nr(parent) : ++ acpi_pci_bus_find_domain_nr(bus); + } + #endif + #endif +diff --git a/include/linux/pci.h b/include/linux/pci.h +index 48839e8..49ba8af 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1390,6 +1390,13 @@ static inline int pci_domain_nr(struct pci_bus *bus) + { + return bus->domain_nr; + } ++/* Arch specific ACPI hook to set-up domain number */ ++#ifdef CONFIG_ACPI ++int acpi_pci_bus_find_domain_nr(struct pci_bus *bus); ++#else ++static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) ++{ return 0; } ++#endif + int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent); + #endif + +-- +2.7.4 + +From bb06753d2e163d8100a017f06a6d9dd195d68a76 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:17 +0200 +Subject: [PATCH 09/11] ARM64/PCI: ACPI support for legacy IRQs parsing and + consolidation with DT code + +To enable PCI legacy IRQs on platforms booting with ACPI, arch code +should include ACPI specific callbacks that parse and set-up the +device IRQ number, equivalent to the DT boot path. Owing to the current +ACPI core scan handlers implementation, ACPI PCI legacy IRQs bindings +cannot be parsed at device add time, since that would trigger ACPI scan +handlers ordering issues depending on how the ACPI tables are defined. + +To solve this problem and consolidate FW PCI legacy IRQs parsing in +one single pcibios callback (pending final removal), this patch moves +DT PCI IRQ parsing to the pcibios_alloc_irq() callback (called by +PCI core code at device probe time) and adds ACPI PCI legacy IRQs +parsing to the same callback too, so that FW PCI legacy IRQs parsing +is confined in one single arch callback that can be easily removed +when code parsing PCI legacy IRQs is consolidated and moved to core +PCI code. + +Signed-off-by: Tomasz Nowicki +Suggested-by: Lorenzo Pieralisi +--- + arch/arm64/kernel/pci.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index d5d3d26..b3b8a2c 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -51,11 +51,16 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) + } + + /* +- * Try to assign the IRQ number from DT when adding a new device ++ * Try to assign the IRQ number when probing a new device + */ +-int pcibios_add_device(struct pci_dev *dev) ++int pcibios_alloc_irq(struct pci_dev *dev) + { +- dev->irq = of_irq_parse_and_map_pci(dev, 0, 0); ++ if (acpi_disabled) ++ dev->irq = of_irq_parse_and_map_pci(dev, 0, 0); ++#ifdef CONFIG_ACPI ++ else ++ return acpi_pci_irq_enable(dev); ++#endif + + return 0; + } +-- +2.7.4 + +From b6e298840f532192a589f8ade128dec3fef3a4c6 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:18 +0200 +Subject: [PATCH 10/11] ARM64/PCI: Implement ACPI low-level calls to access + PCI_Config region from AML + +ACPI spec6.1 - chapter: 5.5.2.4 defines OperationRegion (Declare Operation +Region). Following the spec: " [...] An Operation Region is a specific +region of operation within an address space that is declared as a subset +of the entire address space using a starting address (offset) and a length. +Control methods must have exclusive access to any address accessed via +fields declared in Operation Regions. [...]". + +OperationRegion allows to declare various of operation region address space +identifiers including PCI_Config. PCI_Config is meant to access PCI +configuration space from the ASL. So every time ASL opcode operates +on PCI_Config space region, ASL interpreter dispatches accesses to OS +low-level calls - raw_pci_write() and raw_pci_read() for Linux - so-called +ACPI RAW accessors. + +In order to support PCI_Config operation region, implement mentioned +raw_pci_write() and raw_pci_read() calls so they find associated bus +and call read/write ops. + +Waiting for clarification in the ACPI specifications in relation +to PCI_Config space handling before PCI bus enumeration is completed, +current code does not support PCI_Config region accesses before PCI bus +enumeration whilst providing full AML PCI_Config access availability +when the PCI bus enumeration is completed by the kernel so that +RAW accessors can look-up PCI operations through the struct pci_bus +associated with a PCI bus. + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Jayachandran C +Reviewed-by: Lorenzo Pieralisi +--- + arch/arm64/kernel/pci.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index b3b8a2c..328f857 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -71,13 +71,21 @@ int pcibios_alloc_irq(struct pci_dev *dev) + int raw_pci_read(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) + { +- return -ENXIO; ++ struct pci_bus *b = pci_find_bus(domain, bus); ++ ++ if (!b) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ return b->ops->read(b, devfn, reg, len, val); + } + + int raw_pci_write(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) + { +- return -ENXIO; ++ struct pci_bus *b = pci_find_bus(domain, bus); ++ ++ if (!b) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ return b->ops->write(b, devfn, reg, len, val); + } + + #ifdef CONFIG_NUMA +-- +2.7.4 + +From 3b8cff3fa89ba3ef6f1cf09a0667aa470b7fad0b Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:19 +0200 +Subject: [PATCH 11/11] ARM64/PCI: Support for ACPI based PCI host controller + +Implement pci_acpi_scan_root and other arch-specific call so that ARM64 +can start using ACPI to setup and enumerate PCI buses. + +Prior to buses enumeration the pci_acpi_scan_root() implementation looks +for configuration space start address (obtained through ACPI _CBA method or +MCFG interface). If succeed, it uses ECAM library to create new mapping. +Then it attaches generic ECAM ops (pci_generic_ecam_ops) which are used +for accessing configuration space later on. + +On ARM64, we need to use generic domains (CONFIG_PCI_DOMAINS_GENERIC). +In order to achieve that for ACPI case implement +acpi_pci_bus_find_domain_nr() body so that it retrieves pci_config_window +structure from bus sysdata and eventually gets domain number from +acpi_pci_root structure. + +ACPI requires to run acpi_pci_{add|remove}_bus while new PCI bus is created. +This allows to do some ACPI-specific additional configuration, like +PCI hotplug slot enumeration. In order to fulfill these requirements, +we implement arch-specific pcibios_{add|remove}_bus calls +and call acpi_pci_{add|remove}_bus from there. + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Jayachandran C +Reviewed-by: Lorenzo Pieralisi +--- + arch/arm64/Kconfig | 2 + + arch/arm64/kernel/pci.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 115 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 5a0a691..4806cde 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -3,6 +3,7 @@ config ARM64 + select ACPI_CCA_REQUIRED if ACPI + select ACPI_GENERIC_GSI if ACPI + select ACPI_REDUCED_HARDWARE_ONLY if ACPI ++ select ACPI_MCFG if ACPI + select ARCH_HAS_DEVMEM_IS_ALLOWED + select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE + select ARCH_HAS_ELF_RANDOMIZE +@@ -96,6 +97,7 @@ config ARM64 + select OF_EARLY_FLATTREE + select OF_NUMA if NUMA && OF + select OF_RESERVED_MEM ++ select PCI_ECAM if ACPI + select PERF_USE_VMALLOC + select POWER_RESET + select POWER_SUPPLY +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index 328f857..94cd43c 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -18,6 +18,8 @@ + #include + #include + #include ++#include ++#include + #include + + /* +@@ -100,15 +102,123 @@ EXPORT_SYMBOL(pcibus_to_node); + + #ifdef CONFIG_ACPI + ++struct acpi_pci_generic_root_info { ++ struct acpi_pci_root_info common; ++ struct pci_config_window *cfg; /* config space mapping */ ++}; ++ + int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) + { ++ struct pci_config_window *cfg = bus->sysdata; ++ struct acpi_device *adev = to_acpi_device(cfg->parent); ++ struct acpi_pci_root *root = acpi_driver_data(adev); ++ ++ return root->segment; ++} ++ ++int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) ++{ ++ if (!acpi_disabled) { ++ struct pci_config_window *cfg = bridge->bus->sysdata; ++ struct acpi_device *adev = to_acpi_device(cfg->parent); ++ ACPI_COMPANION_SET(&bridge->dev, adev); ++ } ++ + return 0; + } + +-/* Root bridge scanning */ ++/* ++ * Lookup the bus range for the domain in MCFG, and set up config space ++ * mapping. ++ */ ++static struct pci_config_window * ++pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) ++{ ++ struct resource *bus_res = &root->secondary; ++ u16 seg = root->segment; ++ struct pci_config_window *cfg; ++ struct resource cfgres; ++ unsigned int bsz; ++ ++ /* Use address from _CBA if present, otherwise lookup MCFG */ ++ if (!root->mcfg_addr) ++ root->mcfg_addr = pci_mcfg_lookup(seg, bus_res); ++ ++ if (!root->mcfg_addr) { ++ dev_err(&root->device->dev, "%04x:%pR ECAM region not found\n", ++ seg, bus_res); ++ return NULL; ++ } ++ ++ bsz = 1 << pci_generic_ecam_ops.bus_shift; ++ cfgres.start = root->mcfg_addr + bus_res->start * bsz; ++ cfgres.end = cfgres.start + resource_size(bus_res) * bsz - 1; ++ cfgres.flags = IORESOURCE_MEM; ++ cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, ++ &pci_generic_ecam_ops); ++ if (IS_ERR(cfg)) { ++ dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n", ++ seg, bus_res, PTR_ERR(cfg)); ++ return NULL; ++ } ++ ++ return cfg; ++} ++ ++/* release_info: free resources allocated by init_info */ ++static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci) ++{ ++ struct acpi_pci_generic_root_info *ri; ++ ++ ri = container_of(ci, struct acpi_pci_generic_root_info, common); ++ pci_ecam_free(ri->cfg); ++ kfree(ri); ++} ++ ++static struct acpi_pci_root_ops acpi_pci_root_ops = { ++ .release_info = pci_acpi_generic_release_info, ++}; ++ ++/* Interface called from ACPI code to setup PCI host controller */ + struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) + { +- /* TODO: Should be revisited when implementing PCI on ACPI */ +- return NULL; ++ int node = acpi_get_node(root->device->handle); ++ struct acpi_pci_generic_root_info *ri; ++ struct pci_bus *bus, *child; ++ ++ ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node); ++ if (!ri) ++ return NULL; ++ ++ ri->cfg = pci_acpi_setup_ecam_mapping(root); ++ if (!ri->cfg) { ++ kfree(ri); ++ return NULL; ++ } ++ ++ acpi_pci_root_ops.pci_ops = &ri->cfg->ops->pci_ops; ++ bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &ri->common, ++ ri->cfg); ++ if (!bus) ++ return NULL; ++ ++ pci_bus_size_bridges(bus); ++ pci_bus_assign_resources(bus); ++ ++ list_for_each_entry(child, &bus->children, node) ++ pcie_bus_configure_settings(child); ++ ++ return bus; ++} ++ ++void pcibios_add_bus(struct pci_bus *bus) ++{ ++ acpi_pci_add_bus(bus); + } ++ ++void pcibios_remove_bus(struct pci_bus *bus) ++{ ++ acpi_pci_remove_bus(bus); ++} ++ + #endif +-- +2.7.4 + diff --git a/arm64-pcie-quirks-xgene.patch b/arm64-pcie-quirks-xgene.patch new file mode 100644 index 000000000..8e6805df6 --- /dev/null +++ b/arm64-pcie-quirks-xgene.patch @@ -0,0 +1,508 @@ +From 767b70aa55d013f0c7589955f410d488fed5776a Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 5 Jul 2016 23:49:39 +0100 +Subject: [PATCH 1/4] Some platforms may not be fully compliant with generic + set of PCI config accessors. For these cases we implement the way to + overwrite accessors set. Algorithm traverses available quirk list, matches + against tuple and returns + corresponding PCI config ops. oem_id and oem_table_id come from MCFG table + standard header. All quirks can be defined using DECLARE_ACPI_MCFG_FIXUP() + macro and kept self contained. Example: + +/* Custom PCI config ops */ +static struct pci_generic_ecam_ops foo_pci_ops = { + .bus_shift = 24, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = foo_ecam_config_read, + .write = foo_ecam_config_write, + } +}; + +DECLARE_ACPI_MCFG_FIXUP(&foo_pci_ops, , , , ); + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Dongdong Liu +--- + drivers/acpi/pci_mcfg.c | 41 ++++++++++++++++++++++++++++++++++++--- + include/asm-generic/vmlinux.lds.h | 7 +++++++ + include/linux/pci-acpi.h | 20 +++++++++++++++++++ + 3 files changed, 65 insertions(+), 3 deletions(-) + +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index d3c3e85..deb0077 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -22,6 +22,10 @@ + #include + #include + #include ++#include ++ ++/* Root pointer to the mapped MCFG table */ ++static struct acpi_table_mcfg *mcfg_table; + + /* Structure to hold entries from the MCFG table */ + struct mcfg_entry { +@@ -35,6 +39,38 @@ struct mcfg_entry { + /* List to save mcfg entries */ + static LIST_HEAD(pci_mcfg_list); + ++extern struct pci_cfg_fixup __start_acpi_mcfg_fixups[]; ++extern struct pci_cfg_fixup __end_acpi_mcfg_fixups[]; ++ ++struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) ++{ ++ int bus_num = root->secondary.start; ++ int domain = root->segment; ++ struct pci_cfg_fixup *f; ++ ++ if (!mcfg_table) ++ return &pci_generic_ecam_ops; ++ ++ /* ++ * Match against platform specific quirks and return corresponding ++ * CAM ops. ++ * ++ * First match against PCI topology then use OEM ID and ++ * OEM revision from MCFG table standard header. ++ */ ++ for (f = __start_acpi_mcfg_fixups; f < __end_acpi_mcfg_fixups; f++) { ++ if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) && ++ (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) && ++ (!strncmp(f->oem_id, mcfg_table->header.oem_id, ++ ACPI_OEM_ID_SIZE)) && ++ (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id, ++ ACPI_OEM_TABLE_ID_SIZE))) ++ return f->ops; ++ } ++ /* No quirks, use ECAM */ ++ return &pci_generic_ecam_ops; ++} ++ + phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) + { + struct mcfg_entry *e; +@@ -54,7 +90,6 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) + + static __init int pci_mcfg_parse(struct acpi_table_header *header) + { +- struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *mptr; + struct mcfg_entry *e, *arr; + int i, n; +@@ -64,8 +99,8 @@ static __init int pci_mcfg_parse(struct acpi_table_header *header) + + n = (header->length - sizeof(struct acpi_table_mcfg)) / + sizeof(struct acpi_mcfg_allocation); +- mcfg = (struct acpi_table_mcfg *)header; +- mptr = (struct acpi_mcfg_allocation *) &mcfg[1]; ++ mcfg_table = (struct acpi_table_mcfg *)header; ++ mptr = (struct acpi_mcfg_allocation *) &mcfg_table[1]; + + arr = kcalloc(n, sizeof(*arr), GFP_KERNEL); + if (!arr) +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index 6a67ab9..43604fc 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -300,6 +300,13 @@ + VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \ + } \ + \ ++ /* ACPI MCFG quirks */ \ ++ .acpi_fixup : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .; \ ++ *(.acpi_fixup_mcfg) \ ++ VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .; \ ++ } \ ++ \ + /* Built-in firmware blobs */ \ + .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start_builtin_fw) = .; \ +diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h +index 7d63a66..c8a6559 100644 +--- a/include/linux/pci-acpi.h ++++ b/include/linux/pci-acpi.h +@@ -25,6 +25,7 @@ static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) + extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); + + extern phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res); ++extern struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root); + + static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) + { +@@ -72,6 +73,25 @@ struct acpi_pci_root_ops { + int (*prepare_resources)(struct acpi_pci_root_info *info); + }; + ++struct pci_cfg_fixup { ++ struct pci_ecam_ops *ops; ++ char *oem_id; ++ char *oem_table_id; ++ int domain; ++ int bus_num; ++}; ++ ++#define PCI_MCFG_DOMAIN_ANY -1 ++#define PCI_MCFG_BUS_ANY -1 ++ ++/* Designate a routine to fix up buggy MCFG */ ++#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus) \ ++ static const struct pci_cfg_fixup \ ++ __mcfg_fixup_##oem_id##oem_table_id##dom##bus \ ++ __used __attribute__((__section__(".acpi_fixup_mcfg"), \ ++ aligned((sizeof(void *))))) = \ ++ { ops, oem_id, oem_table_id, dom, bus }; ++ + extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info); + extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, + struct acpi_pci_root_ops *ops, +-- +2.7.4 + +From 4f86a9b006b25dd7336043dab26058ed6fb2802d Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 5 Jul 2016 23:52:46 +0100 +Subject: [PATCH 2/4] pci_generic_ecam_ops is used by default. Since there are + platforms which have non-compliant ECAM space we need to overwrite these + accessors prior to PCI buses enumeration. In order to do that we call + pci_mcfg_get_ops to retrieve pci_ecam_ops structure so that we can use proper + PCI config space accessors and bus_shift. + +pci_generic_ecam_ops is still used for platforms free from quirks. + +Signed-off-by: Tomasz Nowicki +--- + arch/arm64/kernel/pci.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index 94cd43c..a891bda 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -139,6 +139,7 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) + struct pci_config_window *cfg; + struct resource cfgres; + unsigned int bsz; ++ struct pci_ecam_ops *ops; + + /* Use address from _CBA if present, otherwise lookup MCFG */ + if (!root->mcfg_addr) +@@ -150,12 +151,12 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) + return NULL; + } + +- bsz = 1 << pci_generic_ecam_ops.bus_shift; ++ ops = pci_mcfg_get_ops(root); ++ bsz = 1 << ops->bus_shift; + cfgres.start = root->mcfg_addr + bus_res->start * bsz; + cfgres.end = cfgres.start + resource_size(bus_res) * bsz - 1; + cfgres.flags = IORESOURCE_MEM; +- cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, +- &pci_generic_ecam_ops); ++ cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, ops); + if (IS_ERR(cfg)) { + dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n", + seg, bus_res, PTR_ERR(cfg)); +-- +2.7.4 + +From cbdbd697bd6d716eb9d1705ee55445432e73eabb Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 5 Jul 2016 23:53:59 +0100 +Subject: [PATCH 3/4] The ECAM quirk matching criteria per the discussion on + https://lkml.org/lkml/2016/6/13/944 includes: OEM ID, OEM Table ID and OEM + Revision. So this patch adds OEM Table ID into the check to match platform + specific ECAM quirks as well. + +This patch also improve strncmp check using strlen and +min_t to ignore the padding spaces in OEM ID and OEM +Table ID. + +Signed-off-by: Duc Dang +--- + drivers/acpi/pci_mcfg.c | 7 +++++-- + include/linux/pci-acpi.h | 7 ++++--- + 2 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index deb0077..307ca9a 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -62,9 +62,12 @@ struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) + if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) && + (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) && + (!strncmp(f->oem_id, mcfg_table->header.oem_id, +- ACPI_OEM_ID_SIZE)) && ++ min_t(size_t, strlen(f->oem_id), ++ ACPI_OEM_ID_SIZE))) && + (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id, +- ACPI_OEM_TABLE_ID_SIZE))) ++ min_t(size_t, strlen(f->oem_table_id), ++ ACPI_OEM_TABLE_ID_SIZE))) && ++ (f->oem_revision == mcfg_table->header.oem_revision)) + return f->ops; + } + /* No quirks, use ECAM */ +diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h +index c8a6559..5148c8d 100644 +--- a/include/linux/pci-acpi.h ++++ b/include/linux/pci-acpi.h +@@ -77,6 +77,7 @@ struct pci_cfg_fixup { + struct pci_ecam_ops *ops; + char *oem_id; + char *oem_table_id; ++ u32 oem_revision; + int domain; + int bus_num; + }; +@@ -85,12 +86,12 @@ struct pci_cfg_fixup { + #define PCI_MCFG_BUS_ANY -1 + + /* Designate a routine to fix up buggy MCFG */ +-#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus) \ ++#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, rev, dom, bus) \ + static const struct pci_cfg_fixup \ +- __mcfg_fixup_##oem_id##oem_table_id##dom##bus \ ++ __mcfg_fixup_##oem_id##oem_table_id##rev##dom##bus \ + __used __attribute__((__section__(".acpi_fixup_mcfg"), \ + aligned((sizeof(void *))))) = \ +- { ops, oem_id, oem_table_id, dom, bus }; ++ { ops, oem_id, oem_table_id, rev, dom, bus }; + + extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info); + extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, +-- +2.7.4 + +From 78766cf255bc6aafac2f57372a0446f78322da19 Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 5 Jul 2016 23:55:11 +0100 +Subject: [PATCH 4/4] X-Gene PCIe controller does not fully support ECAM. This + patch adds required ECAM fixup to allow X-Gene PCIe controller to be + functional in ACPI boot mode. + +Signed-off-by: Duc Dang +--- + drivers/pci/host/Makefile | 2 +- + drivers/pci/host/pci-xgene-ecam.c | 194 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 195 insertions(+), 1 deletion(-) + create mode 100644 drivers/pci/host/pci-xgene-ecam.c + +diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile +index 9c8698e..3480696 100644 +--- a/drivers/pci/host/Makefile ++++ b/drivers/pci/host/Makefile +@@ -14,7 +14,7 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o + obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o + obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o + obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o +-obj-$(CONFIG_PCI_XGENE) += pci-xgene.o ++obj-$(CONFIG_PCI_XGENE) += pci-xgene.o pci-xgene-ecam.o + obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o + obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o + obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o +diff --git a/drivers/pci/host/pci-xgene-ecam.c b/drivers/pci/host/pci-xgene-ecam.c +new file mode 100644 +index 0000000..1bea63f +--- /dev/null ++++ b/drivers/pci/host/pci-xgene-ecam.c +@@ -0,0 +1,194 @@ ++/* ++ * APM X-Gene PCIe ECAM fixup driver ++ * ++ * Copyright (c) 2016, Applied Micro Circuits Corporation ++ * Author: ++ * Duc Dang ++ * ++ * 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, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_ACPI ++#define RTDID 0x160 ++#define ROOT_CAP_AND_CTRL 0x5C ++ ++/* PCIe IP version */ ++#define XGENE_PCIE_IP_VER_UNKN 0 ++#define XGENE_PCIE_IP_VER_1 1 ++ ++#define APM_OEM_ID "APM" ++#define APM_XGENE_OEM_TABLE_ID "XGENE" ++#define APM_XGENE_OEM_REV 0x00000002 ++ ++struct xgene_pcie_acpi_root { ++ void __iomem *csr_base; ++ u32 version; ++}; ++ ++static acpi_status xgene_pcie_find_csr_base(struct acpi_resource *acpi_res, ++ void *data) ++{ ++ struct xgene_pcie_acpi_root *root = data; ++ struct acpi_resource_fixed_memory32 *fixed32; ++ ++ if (acpi_res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { ++ fixed32 = &acpi_res->data.fixed_memory32; ++ root->csr_base = ioremap(fixed32->address, ++ fixed32->address_length); ++ return AE_CTRL_TERMINATE; ++ } ++ ++ return AE_OK; ++} ++ ++static int xgene_pcie_ecam_init(struct pci_config_window *cfg) ++{ ++ struct xgene_pcie_acpi_root *xgene_root; ++ struct device *dev = cfg->parent; ++ struct acpi_device *adev = to_acpi_device(dev); ++ acpi_handle handle = acpi_device_handle(adev); ++ ++ xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL); ++ if (!xgene_root) ++ return -ENOMEM; ++ ++ acpi_walk_resources(handle, METHOD_NAME__CRS, ++ xgene_pcie_find_csr_base, xgene_root); ++ ++ if (!xgene_root->csr_base) { ++ kfree(xgene_root); ++ return -ENODEV; ++ } ++ ++ xgene_root->version = XGENE_PCIE_IP_VER_1; ++ ++ cfg->priv = xgene_root; ++ ++ return 0; ++} ++ ++/* ++ * For Configuration request, RTDID register is used as Bus Number, ++ * Device Number and Function number of the header fields. ++ */ ++static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn) ++{ ++ struct pci_config_window *cfg = bus->sysdata; ++ struct xgene_pcie_acpi_root *port = cfg->priv; ++ unsigned int b, d, f; ++ u32 rtdid_val = 0; ++ ++ b = bus->number; ++ d = PCI_SLOT(devfn); ++ f = PCI_FUNC(devfn); ++ ++ if (!pci_is_root_bus(bus)) ++ rtdid_val = (b << 8) | (d << 3) | f; ++ ++ writel(rtdid_val, port->csr_base + RTDID); ++ /* read the register back to ensure flush */ ++ readl(port->csr_base + RTDID); ++} ++ ++/* ++ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as ++ * the translation from PCI bus to native BUS. Entire DDR region ++ * is mapped into PCIe space using these registers, so it can be ++ * reached by DMA from EP devices. The BAR0/1 of bridge should be ++ * hidden during enumeration to avoid the sizing and resource allocation ++ * by PCIe core. ++ */ ++static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) ++{ ++ if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) || ++ (offset == PCI_BASE_ADDRESS_1))) ++ return true; ++ ++ return false; ++} ++ ++void __iomem *xgene_pcie_ecam_map_bus(struct pci_bus *bus, ++ unsigned int devfn, int where) ++{ ++ struct pci_config_window *cfg = bus->sysdata; ++ unsigned int busn = bus->number; ++ void __iomem *base; ++ ++ if (busn < cfg->busr.start || busn > cfg->busr.end) ++ return NULL; ++ ++ if ((pci_is_root_bus(bus) && devfn != 0) || ++ xgene_pcie_hide_rc_bars(bus, where)) ++ return NULL; ++ ++ xgene_pcie_set_rtdid_reg(bus, devfn); ++ ++ if (busn > cfg->busr.start) ++ base = cfg->win + (1 << cfg->ops->bus_shift); ++ else ++ base = cfg->win; ++ ++ return base + where; ++} ++ ++static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *val) ++{ ++ struct pci_config_window *cfg = bus->sysdata; ++ struct xgene_pcie_acpi_root *port = cfg->priv; ++ ++ if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) != ++ PCIBIOS_SUCCESSFUL) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ ++ /* ++ * The v1 controller has a bug in its Configuration Request ++ * Retry Status (CRS) logic: when CRS is enabled and we read the ++ * Vendor and Device ID of a non-existent device, the controller ++ * fabricates return data of 0xFFFF0001 ("device exists but is not ++ * ready") instead of 0xFFFFFFFF ("device does not exist"). This ++ * causes the PCI core to retry the read until it times out. ++ * Avoid this by not claiming to support CRS. ++ */ ++ if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) && ++ ((where & ~0x3) == ROOT_CAP_AND_CTRL)) ++ *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); ++ ++ if (size <= 2) ++ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static struct pci_ecam_ops xgene_pcie_ecam_ops = { ++ .bus_shift = 16, ++ .init = xgene_pcie_ecam_init, ++ .pci_ops = { ++ .map_bus = xgene_pcie_ecam_map_bus, ++ .read = xgene_pcie_config_read32, ++ .write = pci_generic_config_write, ++ } ++}; ++ ++DECLARE_ACPI_MCFG_FIXUP(&xgene_pcie_ecam_ops, APM_OEM_ID, ++ APM_XGENE_OEM_TABLE_ID, APM_XGENE_OEM_REV, ++ PCI_MCFG_DOMAIN_ANY, PCI_MCFG_BUS_ANY); ++#endif +-- +2.7.4 + diff --git a/bcm283x-Pull-upstream-fixes-plus-iproc-mmc-driver.patch b/bcm283x-Pull-upstream-fixes-plus-iproc-mmc-driver.patch new file mode 100644 index 000000000..092e7206a --- /dev/null +++ b/bcm283x-Pull-upstream-fixes-plus-iproc-mmc-driver.patch @@ -0,0 +1,3526 @@ +From 735f01326873349426f041a4fa2f5703a1ed43a4 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 5 Feb 2016 15:06:15 -0800 +Subject: [PATCH 01/36] drm/vc4: Fix a framebuffer reference leak on async flip + interrupt. + +We'd need X to queue up an async pageflip while another is +outstanding, and then take a SIGIO. I think X actually avoids sending +out the next pageflip while one's already queued, but I'm not sure. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c +index 018145e..989ee72 100644 +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -544,6 +544,7 @@ static int vc4_async_page_flip(struct drm_crtc *crtc, + /* Make sure all other async modesetes have landed. */ + ret = down_interruptible(&vc4->async_modeset); + if (ret) { ++ drm_framebuffer_unreference(fb); + kfree(flip_state); + return ret; + } +-- +2.7.3 + +From e1ceac2cefbda12d1d9d9ee547fc0cc8bfeebde6 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 12 Feb 2016 14:15:14 -0800 +Subject: [PATCH 02/36] drm/vc4: Bring HDMI up from power off if necessary. + +If the firmware hadn't brought up HDMI for us, we need to do its +power-on reset sequence (reset HD and and clear its STANDBY bits, +reset HDMI, and leave the PHY disabled). + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 29 ++++++++++++++++++++++++++++- + drivers/gpu/drm/vc4/vc4_regs.h | 2 ++ + 2 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c +index c69c046..6e55760 100644 +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -495,6 +495,16 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) + goto err_put_i2c; + } + ++ /* This is the rate that is set by the firmware. The number ++ * needs to be a bit higher than the pixel clock rate ++ * (generally 148.5Mhz). ++ */ ++ ret = clk_set_rate(hdmi->hsm_clock, 163682864); ++ if (ret) { ++ DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); ++ goto err_unprepare_pix; ++ } ++ + ret = clk_prepare_enable(hdmi->hsm_clock); + if (ret) { + DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n", +@@ -516,7 +526,24 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) + vc4->hdmi = hdmi; + + /* HDMI core must be enabled. */ +- WARN_ON_ONCE((HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE) == 0); ++ if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) { ++ HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST); ++ udelay(1); ++ HD_WRITE(VC4_HD_M_CTL, 0); ++ ++ HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE); ++ ++ HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, ++ VC4_HDMI_SW_RESET_HDMI | ++ VC4_HDMI_SW_RESET_FORMAT_DETECT); ++ ++ HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0); ++ ++ /* PHY should be in reset, like ++ * vc4_hdmi_encoder_disable() does. ++ */ ++ HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); ++ } + + drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); +diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h +index 4e52a0a..85c36d2 100644 +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -456,6 +456,8 @@ + #define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 + + #define VC4_HD_M_CTL 0x00c ++# define VC4_HD_M_REGISTER_FILE_STANDBY (3 << 6) ++# define VC4_HD_M_RAM_STANDBY (3 << 4) + # define VC4_HD_M_SW_RST BIT(2) + # define VC4_HD_M_ENABLE BIT(0) + +-- +2.7.3 + +From 63d38d99739736480b24c9f9bd7880ce4e49eb0c Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 12 Feb 2016 15:16:56 -0800 +Subject: [PATCH 03/36] drm/vc4: Add another reg to HDMI debug dumping. + +This is also involved in the HDMI setup sequence so it's nice to see +it. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c +index 6e55760..56272ca 100644 +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -95,6 +95,7 @@ static const struct { + HDMI_REG(VC4_HDMI_SW_RESET_CONTROL), + HDMI_REG(VC4_HDMI_HOTPLUG_INT), + HDMI_REG(VC4_HDMI_HOTPLUG), ++ HDMI_REG(VC4_HDMI_RAM_PACKET_CONFIG), + HDMI_REG(VC4_HDMI_HORZA), + HDMI_REG(VC4_HDMI_HORZB), + HDMI_REG(VC4_HDMI_FIFO_CTL), +-- +2.7.3 + +From 46e96facb9b67486285c26f88ee747b8d9f4abc9 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 15 Feb 2016 17:06:02 -0800 +Subject: [PATCH 04/36] drm/vc4: Fix the name of the VSYNCD_EVEN register. + +It's used for delaying vsync in interlaced mode. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- + drivers/gpu/drm/vc4/vc4_regs.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c +index 989ee72..5e84be2 100644 +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -83,7 +83,7 @@ static const struct { + } crtc_regs[] = { + CRTC_REG(PV_CONTROL), + CRTC_REG(PV_V_CONTROL), +- CRTC_REG(PV_VSYNCD), ++ CRTC_REG(PV_VSYNCD_EVEN), + CRTC_REG(PV_HORZA), + CRTC_REG(PV_HORZB), + CRTC_REG(PV_VERTA), +diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h +index 85c36d2..d529665 100644 +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -187,7 +187,7 @@ + # define PV_VCONTROL_CONTINUOUS BIT(1) + # define PV_VCONTROL_VIDEN BIT(0) + +-#define PV_VSYNCD 0x08 ++#define PV_VSYNCD_EVEN 0x08 + + #define PV_HORZA 0x0c + # define PV_HORZA_HBP_MASK VC4_MASK(31, 16) +-- +2.7.3 + +From baff41935a7b4c1b6015a99a0ca222fd0a5552b9 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 15 Feb 2016 17:31:41 -0800 +Subject: [PATCH 05/36] drm/vc4: Fix setting of vertical timings in the CRTC. + +It looks like when I went to add the interlaced bits, I just took the +existing PV_VERT* block and indented it, instead of copy and pasting +it first. Without this, changing resolution never worked. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c +index 5e84be2..93d53c2 100644 +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -212,6 +212,16 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) + PV_HORZB_HFP) | + VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE)); + ++ CRTC_WRITE(PV_VERTA, ++ VC4_SET_FIELD(mode->vtotal - mode->vsync_end, ++ PV_VERTA_VBP) | ++ VC4_SET_FIELD(mode->vsync_end - mode->vsync_start, ++ PV_VERTA_VSYNC)); ++ CRTC_WRITE(PV_VERTB, ++ VC4_SET_FIELD(mode->vsync_start - mode->vdisplay, ++ PV_VERTB_VFP) | ++ VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE)); ++ + if (interlace) { + CRTC_WRITE(PV_VERTA_EVEN, + VC4_SET_FIELD(mode->vtotal - mode->vsync_end - 1, +-- +2.7.3 + +From 6f7cde6ad6e866660b8e5607a213872e5f34e8fd Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 16 Feb 2016 10:24:08 -0800 +Subject: [PATCH 06/36] drm/vc4: Initialize scaler DISPBKGND on modeset. + +We weren't updating the interlaced bit, so we'd scan out incorrectly +if the firmware had brought up the TV encoder and we were switching to +HDMI. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 6 ++++++ + drivers/gpu/drm/vc4/vc4_regs.h | 14 ++++++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c +index 93d53c2..6ae5abc 100644 +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -183,6 +183,8 @@ static int vc4_get_clock_select(struct drm_crtc *crtc) + + static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) + { ++ struct drm_device *dev = crtc->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct drm_crtc_state *state = crtc->state; + struct drm_display_mode *mode = &state->adjusted_mode; +@@ -251,6 +253,10 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) + PV_CONTROL_FIFO_CLR | + PV_CONTROL_EN); + ++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), ++ SCALER_DISPBKGND_AUTOHS | ++ (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); ++ + if (debug_dump_regs) { + DRM_INFO("CRTC %d regs after:\n", drm_crtc_index(crtc)); + vc4_crtc_dump_regs(vc4_crtc); +diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h +index d529665..7c29993 100644 +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -350,6 +350,17 @@ + # define SCALER_DISPCTRLX_HEIGHT_SHIFT 0 + + #define SCALER_DISPBKGND0 0x00000044 ++# define SCALER_DISPBKGND_AUTOHS BIT(31) ++# define SCALER_DISPBKGND_INTERLACE BIT(30) ++# define SCALER_DISPBKGND_GAMMA BIT(29) ++# define SCALER_DISPBKGND_TESTMODE_MASK VC4_MASK(28, 25) ++# define SCALER_DISPBKGND_TESTMODE_SHIFT 25 ++/* Enables filling the scaler line with the RGB value in the low 24 ++ * bits before compositing. Costs cycles, so should be skipped if ++ * opaque display planes will cover everything. ++ */ ++# define SCALER_DISPBKGND_FILL BIT(24) ++ + #define SCALER_DISPSTAT0 0x00000048 + #define SCALER_DISPBASE0 0x0000004c + # define SCALER_DISPSTATX_MODE_MASK VC4_MASK(31, 30) +@@ -362,6 +373,9 @@ + # define SCALER_DISPSTATX_EMPTY BIT(28) + #define SCALER_DISPCTRL1 0x00000050 + #define SCALER_DISPBKGND1 0x00000054 ++#define SCALER_DISPBKGNDX(x) (SCALER_DISPBKGND0 + \ ++ (x) * (SCALER_DISPBKGND1 - \ ++ SCALER_DISPBKGND0)) + #define SCALER_DISPSTAT1 0x00000058 + #define SCALER_DISPSTATX(x) (SCALER_DISPSTAT0 + \ + (x) * (SCALER_DISPSTAT1 - \ +-- +2.7.3 + +From 449c91f1f06a573ad4a3edd18d7b493bf44478f6 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 28 Dec 2015 14:14:09 -0800 +Subject: [PATCH 07/36] drm/vc4: Improve comments on vc4_plane_state members. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_plane.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index 0addbad..45e353d 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -26,16 +26,19 @@ + + struct vc4_plane_state { + struct drm_plane_state base; ++ /* System memory copy of the display list for this element, computed ++ * at atomic_check time. ++ */ + u32 *dlist; +- u32 dlist_size; /* Number of dwords in allocated for the display list */ ++ u32 dlist_size; /* Number of dwords allocated for the display list */ + u32 dlist_count; /* Number of used dwords in the display list. */ + + /* Offset in the dlist to pointer word 0. */ + u32 pw0_offset; + + /* Offset where the plane's dlist was last stored in the +- hardware at vc4_crtc_atomic_flush() time. +- */ ++ * hardware at vc4_crtc_atomic_flush() time. ++ */ + u32 *hw_dlist; + }; + +-- +2.7.3 + +From 4c8b2ce80659e1c7a75b7b54430dab320aeb440b Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 28 Dec 2015 14:14:57 -0800 +Subject: [PATCH 08/36] drm/vc4: Add missing __iomem annotation to hw_dlist. + +This is the pointer to the HVS device's memory where we stored the +contents of *dlist. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_plane.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index 45e353d..ed07ee5 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -39,7 +39,7 @@ struct vc4_plane_state { + /* Offset where the plane's dlist was last stored in the + * hardware at vc4_crtc_atomic_flush() time. + */ +- u32 *hw_dlist; ++ u32 __iomem *hw_dlist; + }; + + static inline struct vc4_plane_state * +-- +2.7.3 + +From f792f380190638916b495f3051547a849fc97fd2 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 28 Dec 2015 14:34:44 -0800 +Subject: [PATCH 09/36] drm/vc4: Move the plane clipping/scaling setup to a + separate function. + +As we add actual scaling, this is going to get way more complicated. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_plane.c | 78 +++++++++++++++++++++++++++-------------- + 1 file changed, 52 insertions(+), 26 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index ed07ee5..554ed54 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -40,6 +40,14 @@ struct vc4_plane_state { + * hardware at vc4_crtc_atomic_flush() time. + */ + u32 __iomem *hw_dlist; ++ ++ /* Clipped coordinates of the plane on the display. */ ++ int crtc_x, crtc_y, crtc_w, crtc_h; ++ ++ /* Offset to start scanning out from the start of the plane's ++ * BO. ++ */ ++ u32 offset; + }; + + static inline struct vc4_plane_state * +@@ -151,22 +159,17 @@ static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) + vc4_state->dlist[vc4_state->dlist_count++] = val; + } + +-/* Writes out a full display list for an active plane to the plane's +- * private dlist state. +- */ +-static int vc4_plane_mode_set(struct drm_plane *plane, +- struct drm_plane_state *state) ++static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + { + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + struct drm_framebuffer *fb = state->fb; +- struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); +- u32 ctl0_offset = vc4_state->dlist_count; +- const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format); +- uint32_t offset = fb->offsets[0]; +- int crtc_x = state->crtc_x; +- int crtc_y = state->crtc_y; +- int crtc_w = state->crtc_w; +- int crtc_h = state->crtc_h; ++ ++ vc4_state->offset = fb->offsets[0]; ++ ++ vc4_state->crtc_x = state->crtc_x; ++ vc4_state->crtc_y = state->crtc_y; ++ vc4_state->crtc_w = state->crtc_w; ++ vc4_state->crtc_h = state->crtc_h; + + if (state->crtc_w << 16 != state->src_w || + state->crtc_h << 16 != state->src_h) { +@@ -178,18 +181,41 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + return -EINVAL; + } + +- if (crtc_x < 0) { +- offset += drm_format_plane_cpp(fb->pixel_format, 0) * -crtc_x; +- crtc_w += crtc_x; +- crtc_x = 0; ++ if (vc4_state->crtc_x < 0) { ++ vc4_state->offset += (drm_format_plane_cpp(fb->pixel_format, ++ 0) * ++ -vc4_state->crtc_x); ++ vc4_state->crtc_w += vc4_state->crtc_x; ++ vc4_state->crtc_x = 0; + } + +- if (crtc_y < 0) { +- offset += fb->pitches[0] * -crtc_y; +- crtc_h += crtc_y; +- crtc_y = 0; ++ if (vc4_state->crtc_y < 0) { ++ vc4_state->offset += fb->pitches[0] * -vc4_state->crtc_y; ++ vc4_state->crtc_h += vc4_state->crtc_y; ++ vc4_state->crtc_y = 0; + } + ++ return 0; ++} ++ ++ ++/* Writes out a full display list for an active plane to the plane's ++ * private dlist state. ++ */ ++static int vc4_plane_mode_set(struct drm_plane *plane, ++ struct drm_plane_state *state) ++{ ++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); ++ struct drm_framebuffer *fb = state->fb; ++ struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); ++ u32 ctl0_offset = vc4_state->dlist_count; ++ const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format); ++ int ret; ++ ++ ret = vc4_plane_setup_clipping_and_scaling(state); ++ if (ret) ++ return ret; ++ + vc4_dlist_write(vc4_state, + SCALER_CTL0_VALID | + (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | +@@ -199,8 +225,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + /* Position Word 0: Image Positions and Alpha Value */ + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) | +- VC4_SET_FIELD(crtc_x, SCALER_POS0_START_X) | +- VC4_SET_FIELD(crtc_y, SCALER_POS0_START_Y)); ++ VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | ++ VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); + + /* Position Word 1: Scaled Image Dimensions. + * Skipped due to SCALER_CTL0_UNITY scaling. +@@ -212,8 +238,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + SCALER_POS2_ALPHA_MODE_PIPELINE : + SCALER_POS2_ALPHA_MODE_FIXED, + SCALER_POS2_ALPHA_MODE) | +- VC4_SET_FIELD(crtc_w, SCALER_POS2_WIDTH) | +- VC4_SET_FIELD(crtc_h, SCALER_POS2_HEIGHT)); ++ VC4_SET_FIELD(vc4_state->crtc_w, SCALER_POS2_WIDTH) | ++ VC4_SET_FIELD(vc4_state->crtc_h, SCALER_POS2_HEIGHT)); + + /* Position Word 3: Context. Written by the HVS. */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); +@@ -221,7 +247,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + vc4_state->pw0_offset = vc4_state->dlist_count; + + /* Pointer Word 0: RGB / Y Pointer */ +- vc4_dlist_write(vc4_state, bo->paddr + offset); ++ vc4_dlist_write(vc4_state, bo->paddr + vc4_state->offset); + + /* Pointer Context Word 0: Written by the HVS */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); +-- +2.7.3 + +From 696f1db279f08e58bc94172818209d5914e0e2d8 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 30 Dec 2015 11:50:22 -0800 +Subject: [PATCH 10/36] drm/vc4: Add a proper short-circut path for legacy + cursor updates. + +Previously, on every modeset we would allocate new display list +memory, recompute changed planes, write all of them to the new memory, +and pointed scanout at the new list (which will latch approximately at +the next line of scanout). We let +drm_atomic_helper_wait_for_vblanks() decide whether we needed to wait +for a vblank after a modeset before cleaning up the old state and +letting the next modeset proceed, and on legacy cursor updates we +wouldn't wait. If you moved the cursor fast enough, we could +potentially wrap around the display list memory area and overwrite the +existing display list while it was still being scanned out, resulting +in the HVS scanning out garbage or just halting. + +Instead of making cursor updates wait for scanout to move to the new +display list area (which introduces significant cursor lag in X), we +just rewrite our current display list. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_kms.c | 9 ++++ + drivers/gpu/drm/vc4/vc4_plane.c | 94 ++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 96 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c +index f95f2df..4718ae5 100644 +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -49,6 +49,15 @@ vc4_atomic_complete_commit(struct vc4_commit *c) + + drm_atomic_helper_commit_modeset_enables(dev, state); + ++ /* Make sure that drm_atomic_helper_wait_for_vblanks() ++ * actually waits for vblank. If we're doing a full atomic ++ * modeset (as opposed to a vc4_update_plane() short circuit), ++ * then we need to wait for scanout to be done with our ++ * display lists before we free it and potentially reallocate ++ * and overwrite the dlist memory with a new modeset. ++ */ ++ state->legacy_cursor_update = false; ++ + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index 554ed54..713ec00 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -33,8 +33,12 @@ struct vc4_plane_state { + u32 dlist_size; /* Number of dwords allocated for the display list */ + u32 dlist_count; /* Number of used dwords in the display list. */ + +- /* Offset in the dlist to pointer word 0. */ +- u32 pw0_offset; ++ /* Offset in the dlist to various words, for pageflip or ++ * cursor updates. ++ */ ++ u32 pos0_offset; ++ u32 pos2_offset; ++ u32 ptr0_offset; + + /* Offset where the plane's dlist was last stored in the + * hardware at vc4_crtc_atomic_flush() time. +@@ -223,6 +227,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + SCALER_CTL0_UNITY); + + /* Position Word 0: Image Positions and Alpha Value */ ++ vc4_state->pos0_offset = vc4_state->dlist_count; + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) | + VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | +@@ -233,6 +238,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + */ + + /* Position Word 2: Source Image Size, Alpha Mode */ ++ vc4_state->pos2_offset = vc4_state->dlist_count; + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(format->has_alpha ? + SCALER_POS2_ALPHA_MODE_PIPELINE : +@@ -244,9 +250,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + /* Position Word 3: Context. Written by the HVS. */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); + +- vc4_state->pw0_offset = vc4_state->dlist_count; +- + /* Pointer Word 0: RGB / Y Pointer */ ++ vc4_state->ptr0_offset = vc4_state->dlist_count; + vc4_dlist_write(vc4_state, bo->paddr + vc4_state->offset); + + /* Pointer Context Word 0: Written by the HVS */ +@@ -332,13 +337,13 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) + * scanout will start from this address as soon as the FIFO + * needs to refill with pixels. + */ +- writel(addr, &vc4_state->hw_dlist[vc4_state->pw0_offset]); ++ writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]); + + /* Also update the CPU-side dlist copy, so that any later + * atomic updates that don't do a new modeset on our plane + * also use our updated address. + */ +- vc4_state->dlist[vc4_state->pw0_offset] = addr; ++ vc4_state->dlist[vc4_state->ptr0_offset] = addr; + } + + static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { +@@ -354,8 +359,83 @@ static void vc4_plane_destroy(struct drm_plane *plane) + drm_plane_cleanup(plane); + } + ++/* Implements immediate (non-vblank-synced) updates of the cursor ++ * position, or falls back to the atomic helper otherwise. ++ */ ++static int ++vc4_update_plane(struct drm_plane *plane, ++ struct drm_crtc *crtc, ++ struct drm_framebuffer *fb, ++ int crtc_x, int crtc_y, ++ unsigned int crtc_w, unsigned int crtc_h, ++ uint32_t src_x, uint32_t src_y, ++ uint32_t src_w, uint32_t src_h) ++{ ++ struct drm_plane_state *plane_state; ++ struct vc4_plane_state *vc4_state; ++ ++ if (plane != crtc->cursor) ++ goto out; ++ ++ plane_state = plane->state; ++ vc4_state = to_vc4_plane_state(plane_state); ++ ++ if (!plane_state) ++ goto out; ++ ++ /* If we're changing the cursor contents, do that in the ++ * normal vblank-synced atomic path. ++ */ ++ if (fb != plane_state->fb) ++ goto out; ++ ++ /* No configuring new scaling in the fast path. */ ++ if (crtc_w != plane_state->crtc_w || ++ crtc_h != plane_state->crtc_h || ++ src_w != plane_state->src_w || ++ src_h != plane_state->src_h) { ++ goto out; ++ } ++ ++ /* Set the cursor's position on the screen. This is the ++ * expected change from the drm_mode_cursor_universal() ++ * helper. ++ */ ++ plane_state->crtc_x = crtc_x; ++ plane_state->crtc_y = crtc_y; ++ ++ /* Allow changing the start position within the cursor BO, if ++ * that matters. ++ */ ++ plane_state->src_x = src_x; ++ plane_state->src_y = src_y; ++ ++ /* Update the display list based on the new crtc_x/y. */ ++ vc4_plane_atomic_check(plane, plane_state); ++ ++ /* Note that we can't just call vc4_plane_write_dlist() ++ * because that would smash the context data that the HVS is ++ * currently using. ++ */ ++ writel(vc4_state->dlist[vc4_state->pos0_offset], ++ &vc4_state->hw_dlist[vc4_state->pos0_offset]); ++ writel(vc4_state->dlist[vc4_state->pos2_offset], ++ &vc4_state->hw_dlist[vc4_state->pos2_offset]); ++ writel(vc4_state->dlist[vc4_state->ptr0_offset], ++ &vc4_state->hw_dlist[vc4_state->ptr0_offset]); ++ ++ return 0; ++ ++out: ++ return drm_atomic_helper_update_plane(plane, crtc, fb, ++ crtc_x, crtc_y, ++ crtc_w, crtc_h, ++ src_x, src_y, ++ src_w, src_h); ++} ++ + static const struct drm_plane_funcs vc4_plane_funcs = { +- .update_plane = drm_atomic_helper_update_plane, ++ .update_plane = vc4_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vc4_plane_destroy, + .set_property = NULL, +-- +2.7.3 + +From cd30019db690e3a92fe5d7d771352f118a105f82 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 28 Dec 2015 13:25:41 -0800 +Subject: [PATCH 11/36] drm/vc4: Make the CRTCs cooperate on allocating display + lists. + +So far, we've only ever lit up one CRTC, so this has been fine. To +extend to more displays or more planes, we need to make sure we don't +run our display lists into each other. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 115 +++++++++++++++++++++++------------------ + drivers/gpu/drm/vc4/vc4_drv.h | 8 ++- + drivers/gpu/drm/vc4/vc4_hvs.c | 13 +++++ + 3 files changed, 84 insertions(+), 52 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c +index 6ae5abc..9032c06 100644 +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -49,22 +49,27 @@ struct vc4_crtc { + /* Which HVS channel we're using for our CRTC. */ + int channel; + +- /* Pointer to the actual hardware display list memory for the +- * crtc. +- */ +- u32 __iomem *dlist; +- +- u32 dlist_size; /* in dwords */ +- + struct drm_pending_vblank_event *event; + }; + ++struct vc4_crtc_state { ++ struct drm_crtc_state base; ++ /* Dlist area for this CRTC configuration. */ ++ struct drm_mm_node mm; ++}; ++ + static inline struct vc4_crtc * + to_vc4_crtc(struct drm_crtc *crtc) + { + return (struct vc4_crtc *)crtc; + } + ++static inline struct vc4_crtc_state * ++to_vc4_crtc_state(struct drm_crtc_state *crtc_state) ++{ ++ return (struct vc4_crtc_state *)crtc_state; ++} ++ + struct vc4_crtc_data { + /* Which channel of the HVS this pixelvalve sources from. */ + int hvs_channel; +@@ -335,11 +340,13 @@ static void vc4_crtc_enable(struct drm_crtc *crtc) + static int vc4_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) + { ++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_plane *plane; +- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ unsigned long flags; + u32 dlist_count = 0; ++ int ret; + + /* The pixelvalve can only feed one encoder (and encoders are + * 1:1 with connectors.) +@@ -362,18 +369,12 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, + + dlist_count++; /* Account for SCALER_CTL0_END. */ + +- if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) { +- vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist + +- HVS_BOOTLOADER_DLIST_END); +- vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) - +- HVS_BOOTLOADER_DLIST_END); +- +- if (dlist_count > vc4_crtc->dlist_size) { +- DRM_DEBUG_KMS("dlist too large for CRTC (%d > %d).\n", +- dlist_count, vc4_crtc->dlist_size); +- return -EINVAL; +- } +- } ++ spin_lock_irqsave(&vc4->hvs->mm_lock, flags); ++ ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, ++ dlist_count, 1, 0); ++ spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); ++ if (ret) ++ return ret; + + return 0; + } +@@ -384,47 +385,29 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + struct drm_plane *plane; + bool debug_dump_regs = false; +- u32 __iomem *dlist_next = vc4_crtc->dlist; ++ u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; ++ u32 __iomem *dlist_next = dlist_start; + + if (debug_dump_regs) { + DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); + vc4_hvs_dump_state(dev); + } + +- /* Copy all the active planes' dlist contents to the hardware dlist. +- * +- * XXX: If the new display list was large enough that it +- * overlapped a currently-read display list, we need to do +- * something like disable scanout before putting in the new +- * list. For now, we're safe because we only have the two +- * planes. +- */ ++ /* Copy all the active planes' dlist contents to the hardware dlist. */ + drm_atomic_crtc_for_each_plane(plane, crtc) { + dlist_next += vc4_plane_write_dlist(plane, dlist_next); + } + +- if (dlist_next == vc4_crtc->dlist) { +- /* If no planes were enabled, use the SCALER_CTL0_END +- * at the start of the display list memory (in the +- * bootloader section). We'll rewrite that +- * SCALER_CTL0_END, just in case, though. +- */ +- writel(SCALER_CTL0_END, vc4->hvs->dlist); +- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), 0); +- } else { +- writel(SCALER_CTL0_END, dlist_next); +- dlist_next++; +- +- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), +- (u32 __iomem *)vc4_crtc->dlist - +- (u32 __iomem *)vc4->hvs->dlist); +- +- /* Make the next display list start after ours. */ +- vc4_crtc->dlist_size -= (dlist_next - vc4_crtc->dlist); +- vc4_crtc->dlist = dlist_next; +- } ++ writel(SCALER_CTL0_END, dlist_next); ++ dlist_next++; ++ ++ WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); ++ ++ HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), ++ vc4_state->mm.start); + + if (debug_dump_regs) { + DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); +@@ -590,6 +573,36 @@ static int vc4_page_flip(struct drm_crtc *crtc, + return drm_atomic_helper_page_flip(crtc, fb, event, flags); + } + ++static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) ++{ ++ struct vc4_crtc_state *vc4_state; ++ ++ vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL); ++ if (!vc4_state) ++ return NULL; ++ ++ __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base); ++ return &vc4_state->base; ++} ++ ++static void vc4_crtc_destroy_state(struct drm_crtc *crtc, ++ struct drm_crtc_state *state) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(crtc->dev); ++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); ++ ++ if (vc4_state->mm.allocated) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&vc4->hvs->mm_lock, flags); ++ drm_mm_remove_node(&vc4_state->mm); ++ spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); ++ ++ } ++ ++ __drm_atomic_helper_crtc_destroy_state(crtc, state); ++} ++ + static const struct drm_crtc_funcs vc4_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = vc4_crtc_destroy, +@@ -598,8 +611,8 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = { + .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ + .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ + .reset = drm_atomic_helper_crtc_reset, +- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, +- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, ++ .atomic_duplicate_state = vc4_crtc_duplicate_state, ++ .atomic_destroy_state = vc4_crtc_destroy_state, + }; + + static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index 51a6333..38a31c7 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -154,7 +154,13 @@ struct vc4_v3d { + struct vc4_hvs { + struct platform_device *pdev; + void __iomem *regs; +- void __iomem *dlist; ++ u32 __iomem *dlist; ++ ++ /* Memory manager for CRTCs to allocate space in the display ++ * list. Units are dwords. ++ */ ++ struct drm_mm dlist_mm; ++ spinlock_t mm_lock; + }; + + struct vc4_plane { +diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c +index 8098c5b..9e43554 100644 +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -119,6 +119,17 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) + + hvs->dlist = hvs->regs + SCALER_DLIST_START; + ++ spin_lock_init(&hvs->mm_lock); ++ ++ /* Set up the HVS display list memory manager. We never ++ * overwrite the setup from the bootloader (just 128b out of ++ * our 16K), since we don't want to scramble the screen when ++ * transitioning from the firmware's boot setup to runtime. ++ */ ++ drm_mm_init(&hvs->dlist_mm, ++ HVS_BOOTLOADER_DLIST_END, ++ (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); ++ + vc4->hvs = hvs; + return 0; + } +@@ -129,6 +140,8 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master, + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; + ++ drm_mm_takedown(&vc4->hvs->dlist_mm); ++ + vc4->hvs = NULL; + } + +-- +2.7.3 + +From 7934fe9bdbbe2ffb4bcfe656a22a8f9f4e3d266a Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 28 Dec 2015 14:45:25 -0800 +Subject: [PATCH 12/36] drm/vc4: Fix which value is being used for source image + size. + +This doesn't matter yet since we only allow 1:1 scaling, but the +comment clearly says we should be using the source size. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_plane.c | 23 ++++++++++++++--------- + 1 file changed, 14 insertions(+), 9 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index 713ec00..d9c9290 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -47,6 +47,8 @@ struct vc4_plane_state { + + /* Clipped coordinates of the plane on the display. */ + int crtc_x, crtc_y, crtc_w, crtc_h; ++ /* Clipped size of the area scanned from in the FB. */ ++ u32 src_w, src_h; + + /* Offset to start scanning out from the start of the plane's + * BO. +@@ -170,11 +172,6 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + + vc4_state->offset = fb->offsets[0]; + +- vc4_state->crtc_x = state->crtc_x; +- vc4_state->crtc_y = state->crtc_y; +- vc4_state->crtc_w = state->crtc_w; +- vc4_state->crtc_h = state->crtc_h; +- + if (state->crtc_w << 16 != state->src_w || + state->crtc_h << 16 != state->src_h) { + /* We don't support scaling yet, which involves +@@ -185,17 +182,25 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + return -EINVAL; + } + ++ vc4_state->src_w = state->src_w >> 16; ++ vc4_state->src_h = state->src_h >> 16; ++ ++ vc4_state->crtc_x = state->crtc_x; ++ vc4_state->crtc_y = state->crtc_y; ++ vc4_state->crtc_w = state->crtc_w; ++ vc4_state->crtc_h = state->crtc_h; ++ + if (vc4_state->crtc_x < 0) { + vc4_state->offset += (drm_format_plane_cpp(fb->pixel_format, + 0) * + -vc4_state->crtc_x); +- vc4_state->crtc_w += vc4_state->crtc_x; ++ vc4_state->src_w += vc4_state->crtc_x; + vc4_state->crtc_x = 0; + } + + if (vc4_state->crtc_y < 0) { + vc4_state->offset += fb->pitches[0] * -vc4_state->crtc_y; +- vc4_state->crtc_h += vc4_state->crtc_y; ++ vc4_state->src_h += vc4_state->crtc_y; + vc4_state->crtc_y = 0; + } + +@@ -244,8 +249,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + SCALER_POS2_ALPHA_MODE_PIPELINE : + SCALER_POS2_ALPHA_MODE_FIXED, + SCALER_POS2_ALPHA_MODE) | +- VC4_SET_FIELD(vc4_state->crtc_w, SCALER_POS2_WIDTH) | +- VC4_SET_FIELD(vc4_state->crtc_h, SCALER_POS2_HEIGHT)); ++ VC4_SET_FIELD(vc4_state->src_w, SCALER_POS2_WIDTH) | ++ VC4_SET_FIELD(vc4_state->src_h, SCALER_POS2_HEIGHT)); + + /* Position Word 3: Context. Written by the HVS. */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); +-- +2.7.3 + +From e710e8e1d13c85a635c09168df6d008955ac5a4e Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 20 Oct 2015 16:06:57 +0100 +Subject: [PATCH 13/36] drm/vc4: Add support for scaling of display planes. + +This implements a simple policy for choosing scaling modes +(trapezoidal for decimation, PPF for magnification), and a single PPF +filter (Mitchell/Netravali's recommendation). + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_drv.h | 4 + + drivers/gpu/drm/vc4/vc4_hvs.c | 84 +++++++++++++ + drivers/gpu/drm/vc4/vc4_plane.c | 253 +++++++++++++++++++++++++++++++++++++--- + drivers/gpu/drm/vc4/vc4_regs.h | 46 ++++++++ + 4 files changed, 374 insertions(+), 13 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index 38a31c7..83db0b7 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -160,7 +160,11 @@ struct vc4_hvs { + * list. Units are dwords. + */ + struct drm_mm dlist_mm; ++ /* Memory manager for the LBM memory used by HVS scaling. */ ++ struct drm_mm lbm_mm; + spinlock_t mm_lock; ++ ++ struct drm_mm_node mitchell_netravali_filter; + }; + + struct vc4_plane { +diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c +index 9e43554..6fbab1c 100644 +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -100,12 +100,76 @@ int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused) + } + #endif + ++/* The filter kernel is composed of dwords each containing 3 9-bit ++ * signed integers packed next to each other. ++ */ ++#define VC4_INT_TO_COEFF(coeff) (coeff & 0x1ff) ++#define VC4_PPF_FILTER_WORD(c0, c1, c2) \ ++ ((((c0) & 0x1ff) << 0) | \ ++ (((c1) & 0x1ff) << 9) | \ ++ (((c2) & 0x1ff) << 18)) ++ ++/* The whole filter kernel is arranged as the coefficients 0-16 going ++ * up, then a pad, then 17-31 going down and reversed within the ++ * dwords. This means that a linear phase kernel (where it's ++ * symmetrical at the boundary between 15 and 16) has the last 5 ++ * dwords matching the first 5, but reversed. ++ */ ++#define VC4_LINEAR_PHASE_KERNEL(c0, c1, c2, c3, c4, c5, c6, c7, c8, \ ++ c9, c10, c11, c12, c13, c14, c15) \ ++ {VC4_PPF_FILTER_WORD(c0, c1, c2), \ ++ VC4_PPF_FILTER_WORD(c3, c4, c5), \ ++ VC4_PPF_FILTER_WORD(c6, c7, c8), \ ++ VC4_PPF_FILTER_WORD(c9, c10, c11), \ ++ VC4_PPF_FILTER_WORD(c12, c13, c14), \ ++ VC4_PPF_FILTER_WORD(c15, c15, 0)} ++ ++#define VC4_LINEAR_PHASE_KERNEL_DWORDS 6 ++#define VC4_KERNEL_DWORDS (VC4_LINEAR_PHASE_KERNEL_DWORDS * 2 - 1) ++ ++/* Recommended B=1/3, C=1/3 filter choice from Mitchell/Netravali. ++ * http://www.cs.utexas.edu/~fussell/courses/cs384g/lectures/mitchell/Mitchell.pdf ++ */ ++static const u32 mitchell_netravali_1_3_1_3_kernel[] = ++ VC4_LINEAR_PHASE_KERNEL(0, -2, -6, -8, -10, -8, -3, 2, 18, ++ 50, 82, 119, 155, 187, 213, 227); ++ ++static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, ++ struct drm_mm_node *space, ++ const u32 *kernel) ++{ ++ int ret, i; ++ u32 __iomem *dst_kernel; ++ ++ ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS, 1, ++ 0); ++ if (ret) { ++ DRM_ERROR("Failed to allocate space for filter kernel: %d\n", ++ ret); ++ return ret; ++ } ++ ++ dst_kernel = hvs->dlist + space->start; ++ ++ for (i = 0; i < VC4_KERNEL_DWORDS; i++) { ++ if (i < VC4_LINEAR_PHASE_KERNEL_DWORDS) ++ writel(kernel[i], &dst_kernel[i]); ++ else { ++ writel(kernel[VC4_KERNEL_DWORDS - i - 1], ++ &dst_kernel[i]); ++ } ++ } ++ ++ return 0; ++} ++ + static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) + { + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; + struct vc4_hvs *hvs = NULL; ++ int ret; + + hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); + if (!hvs) +@@ -130,6 +194,22 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) + HVS_BOOTLOADER_DLIST_END, + (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); + ++ /* Set up the HVS LBM memory manager. We could have some more ++ * complicated data structure that allowed reuse of LBM areas ++ * between planes when they don't overlap on the screen, but ++ * for now we just allocate globally. ++ */ ++ drm_mm_init(&hvs->lbm_mm, 0, 96 * 1024); ++ ++ /* Upload filter kernels. We only have the one for now, so we ++ * keep it around for the lifetime of the driver. ++ */ ++ ret = vc4_hvs_upload_linear_kernel(hvs, ++ &hvs->mitchell_netravali_filter, ++ mitchell_netravali_1_3_1_3_kernel); ++ if (ret) ++ return ret; ++ + vc4->hvs = hvs; + return 0; + } +@@ -140,7 +220,11 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master, + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; + ++ if (vc4->hvs->mitchell_netravali_filter.allocated) ++ drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter); ++ + drm_mm_takedown(&vc4->hvs->dlist_mm); ++ drm_mm_takedown(&vc4->hvs->lbm_mm); + + vc4->hvs = NULL; + } +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index d9c9290..7c2d697 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -24,6 +24,12 @@ + #include "drm_fb_cma_helper.h" + #include "drm_plane_helper.h" + ++enum vc4_scaling_mode { ++ VC4_SCALING_NONE, ++ VC4_SCALING_TPZ, ++ VC4_SCALING_PPF, ++}; ++ + struct vc4_plane_state { + struct drm_plane_state base; + /* System memory copy of the display list for this element, computed +@@ -47,13 +53,19 @@ struct vc4_plane_state { + + /* Clipped coordinates of the plane on the display. */ + int crtc_x, crtc_y, crtc_w, crtc_h; +- /* Clipped size of the area scanned from in the FB. */ +- u32 src_w, src_h; ++ /* Clipped area being scanned from in the FB. */ ++ u32 src_x, src_y, src_w, src_h; ++ ++ enum vc4_scaling_mode x_scaling, y_scaling; ++ bool is_unity; + + /* Offset to start scanning out from the start of the plane's + * BO. + */ + u32 offset; ++ ++ /* Our allocation in LBM for temporary storage during scaling. */ ++ struct drm_mm_node lbm; + }; + + static inline struct vc4_plane_state * +@@ -90,6 +102,16 @@ static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) + return NULL; + } + ++static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) ++{ ++ if (dst > src) ++ return VC4_SCALING_PPF; ++ else if (dst < src) ++ return VC4_SCALING_TPZ; ++ else ++ return VC4_SCALING_NONE; ++} ++ + static bool plane_enabled(struct drm_plane_state *state) + { + return state->fb && state->crtc; +@@ -106,6 +128,8 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane + if (!vc4_state) + return NULL; + ++ memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm)); ++ + __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base); + + if (vc4_state->dlist) { +@@ -125,8 +149,17 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane + static void vc4_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) + { ++ struct vc4_dev *vc4 = to_vc4_dev(plane->dev); + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + ++ if (vc4_state->lbm.allocated) { ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); ++ drm_mm_remove_node(&vc4_state->lbm); ++ spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); ++ } ++ + kfree(vc4_state->dlist); + __drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base); + kfree(state); +@@ -165,23 +198,60 @@ static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) + vc4_state->dlist[vc4_state->dlist_count++] = val; + } + ++/* Returns the scl0/scl1 field based on whether the dimensions need to ++ * be up/down/non-scaled. ++ * ++ * This is a replication of a table from the spec. ++ */ ++static u32 vc4_get_scl_field(struct drm_plane_state *state) ++{ ++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); ++ ++ switch (vc4_state->x_scaling << 2 | vc4_state->y_scaling) { ++ case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF: ++ return SCALER_CTL0_SCL_H_PPF_V_PPF; ++ case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF: ++ return SCALER_CTL0_SCL_H_TPZ_V_PPF; ++ case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ: ++ return SCALER_CTL0_SCL_H_PPF_V_TPZ; ++ case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ: ++ return SCALER_CTL0_SCL_H_TPZ_V_TPZ; ++ case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE: ++ return SCALER_CTL0_SCL_H_PPF_V_NONE; ++ case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF: ++ return SCALER_CTL0_SCL_H_NONE_V_PPF; ++ case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ: ++ return SCALER_CTL0_SCL_H_NONE_V_TPZ; ++ case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE: ++ return SCALER_CTL0_SCL_H_TPZ_V_NONE; ++ default: ++ case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE: ++ /* The unity case is independently handled by ++ * SCALER_CTL0_UNITY. ++ */ ++ return 0; ++ } ++} ++ + static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + { ++ struct drm_plane *plane = state->plane; + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + struct drm_framebuffer *fb = state->fb; ++ u32 subpixel_src_mask = (1 << 16) - 1; + + vc4_state->offset = fb->offsets[0]; + +- if (state->crtc_w << 16 != state->src_w || +- state->crtc_h << 16 != state->src_h) { +- /* We don't support scaling yet, which involves +- * allocating the LBM memory for scaling temporary +- * storage, and putting filter kernels in the HVS +- * context. +- */ ++ /* We don't support subpixel source positioning for scaling. */ ++ if ((state->src_x & subpixel_src_mask) || ++ (state->src_y & subpixel_src_mask) || ++ (state->src_w & subpixel_src_mask) || ++ (state->src_h & subpixel_src_mask)) { + return -EINVAL; + } + ++ vc4_state->src_x = state->src_x >> 16; ++ vc4_state->src_y = state->src_y >> 16; + vc4_state->src_w = state->src_w >> 16; + vc4_state->src_h = state->src_h >> 16; + +@@ -190,6 +260,23 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + vc4_state->crtc_w = state->crtc_w; + vc4_state->crtc_h = state->crtc_h; + ++ vc4_state->x_scaling = vc4_get_scaling_mode(vc4_state->src_w, ++ vc4_state->crtc_w); ++ vc4_state->y_scaling = vc4_get_scaling_mode(vc4_state->src_h, ++ vc4_state->crtc_h); ++ vc4_state->is_unity = (vc4_state->x_scaling == VC4_SCALING_NONE && ++ vc4_state->y_scaling == VC4_SCALING_NONE); ++ ++ /* No configuring scaling on the cursor plane, since it gets ++ non-vblank-synced updates, and scaling requires requires ++ LBM changes which have to be vblank-synced. ++ */ ++ if (plane->type == DRM_PLANE_TYPE_CURSOR && !vc4_state->is_unity) ++ return -EINVAL; ++ ++ /* Clamp the on-screen start x/y to 0. The hardware doesn't ++ * support negative y, and negative x wastes bandwidth. ++ */ + if (vc4_state->crtc_x < 0) { + vc4_state->offset += (drm_format_plane_cpp(fb->pixel_format, + 0) * +@@ -207,6 +294,87 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + return 0; + } + ++static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst) ++{ ++ u32 scale, recip; ++ ++ scale = (1 << 16) * src / dst; ++ ++ /* The specs note that while the reciprocal would be defined ++ * as (1<<32)/scale, ~0 is close enough. ++ */ ++ recip = ~0 / scale; ++ ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) | ++ VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE)); ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP)); ++} ++ ++static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst) ++{ ++ u32 scale = (1 << 16) * src / dst; ++ ++ vc4_dlist_write(vc4_state, ++ SCALER_PPF_AGC | ++ VC4_SET_FIELD(scale, SCALER_PPF_SCALE) | ++ VC4_SET_FIELD(0, SCALER_PPF_IPHASE)); ++} ++ ++static u32 vc4_lbm_size(struct drm_plane_state *state) ++{ ++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); ++ /* This is the worst case number. One of the two sizes will ++ * be used depending on the scaling configuration. ++ */ ++ u32 pix_per_line = max(vc4_state->src_w, (u32)vc4_state->crtc_w); ++ u32 lbm; ++ ++ if (vc4_state->is_unity) ++ return 0; ++ else if (vc4_state->y_scaling == VC4_SCALING_TPZ) ++ lbm = pix_per_line * 8; ++ else { ++ /* In special cases, this multiplier might be 12. */ ++ lbm = pix_per_line * 16; ++ } ++ ++ lbm = roundup(lbm, 32); ++ ++ return lbm; ++} ++ ++static void vc4_write_scaling_parameters(struct drm_plane_state *state) ++{ ++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); ++ ++ /* Ch0 H-PPF Word 0: Scaling Parameters */ ++ if (vc4_state->x_scaling == VC4_SCALING_PPF) { ++ vc4_write_ppf(vc4_state, ++ vc4_state->src_w, vc4_state->crtc_w); ++ } ++ ++ /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */ ++ if (vc4_state->y_scaling == VC4_SCALING_PPF) { ++ vc4_write_ppf(vc4_state, ++ vc4_state->src_h, vc4_state->crtc_h); ++ vc4_dlist_write(vc4_state, 0xc0c0c0c0); ++ } ++ ++ /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */ ++ if (vc4_state->x_scaling == VC4_SCALING_TPZ) { ++ vc4_write_tpz(vc4_state, ++ vc4_state->src_w, vc4_state->crtc_w); ++ } ++ ++ /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */ ++ if (vc4_state->y_scaling == VC4_SCALING_TPZ) { ++ vc4_write_tpz(vc4_state, ++ vc4_state->src_h, vc4_state->crtc_h); ++ vc4_dlist_write(vc4_state, 0xc0c0c0c0); ++ } ++} + + /* Writes out a full display list for an active plane to the plane's + * private dlist state. +@@ -214,22 +382,50 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + static int vc4_plane_mode_set(struct drm_plane *plane, + struct drm_plane_state *state) + { ++ struct vc4_dev *vc4 = to_vc4_dev(plane->dev); + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); + u32 ctl0_offset = vc4_state->dlist_count; + const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format); ++ u32 scl; ++ u32 lbm_size; ++ unsigned long irqflags; + int ret; + + ret = vc4_plane_setup_clipping_and_scaling(state); + if (ret) + return ret; + ++ /* Allocate the LBM memory that the HVS will use for temporary ++ * storage due to our scaling/format conversion. ++ */ ++ lbm_size = vc4_lbm_size(state); ++ if (lbm_size) { ++ if (!vc4_state->lbm.allocated) { ++ spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); ++ ret = drm_mm_insert_node(&vc4->hvs->lbm_mm, ++ &vc4_state->lbm, ++ lbm_size, 32, 0); ++ spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); ++ } else { ++ WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); ++ } ++ } ++ ++ if (ret) ++ return ret; ++ ++ scl = vc4_get_scl_field(state); ++ ++ /* Control word */ + vc4_dlist_write(vc4_state, + SCALER_CTL0_VALID | + (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | + (format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | +- SCALER_CTL0_UNITY); ++ (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) | ++ VC4_SET_FIELD(scl, SCALER_CTL0_SCL0) | ++ VC4_SET_FIELD(scl, SCALER_CTL0_SCL1)); + + /* Position Word 0: Image Positions and Alpha Value */ + vc4_state->pos0_offset = vc4_state->dlist_count; +@@ -238,9 +434,14 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | + VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); + +- /* Position Word 1: Scaled Image Dimensions. +- * Skipped due to SCALER_CTL0_UNITY scaling. +- */ ++ /* Position Word 1: Scaled Image Dimensions. */ ++ if (!vc4_state->is_unity) { ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(vc4_state->crtc_w, ++ SCALER_POS1_SCL_WIDTH) | ++ VC4_SET_FIELD(vc4_state->crtc_h, ++ SCALER_POS1_SCL_HEIGHT)); ++ } + + /* Position Word 2: Source Image Size, Alpha Mode */ + vc4_state->pos2_offset = vc4_state->dlist_count; +@@ -266,6 +467,32 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH)); + ++ if (!vc4_state->is_unity) { ++ /* LBM Base Address. */ ++ if (vc4_state->y_scaling != VC4_SCALING_NONE) ++ vc4_dlist_write(vc4_state, vc4_state->lbm.start); ++ ++ vc4_write_scaling_parameters(state); ++ ++ /* If any PPF setup was done, then all the kernel ++ * pointers get uploaded. ++ */ ++ if (vc4_state->x_scaling == VC4_SCALING_PPF || ++ vc4_state->y_scaling == VC4_SCALING_PPF) { ++ u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start, ++ SCALER_PPF_KERNEL_OFFSET); ++ ++ /* HPPF plane 0 */ ++ vc4_dlist_write(vc4_state, kernel); ++ /* VPPF plane 0 */ ++ vc4_dlist_write(vc4_state, kernel); ++ /* HPPF plane 1 */ ++ vc4_dlist_write(vc4_state, kernel); ++ /* VPPF plane 1 */ ++ vc4_dlist_write(vc4_state, kernel); ++ } ++ } ++ + vc4_state->dlist[ctl0_offset] |= + VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE); + +diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h +index 7c29993..a5b544d 100644 +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -552,6 +552,21 @@ enum hvs_pixel_format { + #define SCALER_CTL0_ORDER_MASK VC4_MASK(14, 13) + #define SCALER_CTL0_ORDER_SHIFT 13 + ++#define SCALER_CTL0_SCL1_MASK VC4_MASK(10, 8) ++#define SCALER_CTL0_SCL1_SHIFT 8 ++ ++#define SCALER_CTL0_SCL0_MASK VC4_MASK(7, 5) ++#define SCALER_CTL0_SCL0_SHIFT 5 ++ ++#define SCALER_CTL0_SCL_H_PPF_V_PPF 0 ++#define SCALER_CTL0_SCL_H_TPZ_V_PPF 1 ++#define SCALER_CTL0_SCL_H_PPF_V_TPZ 2 ++#define SCALER_CTL0_SCL_H_TPZ_V_TPZ 3 ++#define SCALER_CTL0_SCL_H_PPF_V_NONE 4 ++#define SCALER_CTL0_SCL_H_NONE_V_PPF 5 ++#define SCALER_CTL0_SCL_H_NONE_V_TPZ 6 ++#define SCALER_CTL0_SCL_H_TPZ_V_NONE 7 ++ + /* Set to indicate no scaling. */ + #define SCALER_CTL0_UNITY BIT(4) + +@@ -567,6 +582,12 @@ enum hvs_pixel_format { + #define SCALER_POS0_START_X_MASK VC4_MASK(11, 0) + #define SCALER_POS0_START_X_SHIFT 0 + ++#define SCALER_POS1_SCL_HEIGHT_MASK VC4_MASK(27, 16) ++#define SCALER_POS1_SCL_HEIGHT_SHIFT 16 ++ ++#define SCALER_POS1_SCL_WIDTH_MASK VC4_MASK(11, 0) ++#define SCALER_POS1_SCL_WIDTH_SHIFT 0 ++ + #define SCALER_POS2_ALPHA_MODE_MASK VC4_MASK(31, 30) + #define SCALER_POS2_ALPHA_MODE_SHIFT 30 + #define SCALER_POS2_ALPHA_MODE_PIPELINE 0 +@@ -580,6 +601,31 @@ enum hvs_pixel_format { + #define SCALER_POS2_WIDTH_MASK VC4_MASK(11, 0) + #define SCALER_POS2_WIDTH_SHIFT 0 + ++#define SCALER_TPZ0_VERT_RECALC BIT(31) ++#define SCALER_TPZ0_SCALE_MASK VC4_MASK(28, 8) ++#define SCALER_TPZ0_SCALE_SHIFT 8 ++#define SCALER_TPZ0_IPHASE_MASK VC4_MASK(7, 0) ++#define SCALER_TPZ0_IPHASE_SHIFT 0 ++#define SCALER_TPZ1_RECIP_MASK VC4_MASK(15, 0) ++#define SCALER_TPZ1_RECIP_SHIFT 0 ++ ++/* Skips interpolating coefficients to 64 phases, so just 8 are used. ++ * Required for nearest neighbor. ++ */ ++#define SCALER_PPF_NOINTERP BIT(31) ++/* Replaes the highest valued coefficient with one that makes all 4 ++ * sum to unity. ++ */ ++#define SCALER_PPF_AGC BIT(30) ++#define SCALER_PPF_SCALE_MASK VC4_MASK(24, 8) ++#define SCALER_PPF_SCALE_SHIFT 8 ++#define SCALER_PPF_IPHASE_MASK VC4_MASK(6, 0) ++#define SCALER_PPF_IPHASE_SHIFT 0 ++ ++#define SCALER_PPF_KERNEL_OFFSET_MASK VC4_MASK(13, 0) ++#define SCALER_PPF_KERNEL_OFFSET_SHIFT 0 ++#define SCALER_PPF_KERNEL_UNCACHED BIT(31) ++ + #define SCALER_SRC_PITCH_MASK VC4_MASK(15, 0) + #define SCALER_SRC_PITCH_SHIFT 0 + +-- +2.7.3 + +From c1f11c3b1a4379841341911c379237d3a3870607 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 20 Oct 2015 13:59:15 +0100 +Subject: [PATCH 14/36] drm/vc4: Add support a few more RGB display plane + formats. + +These were all touch-tested with modetest. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_plane.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index 7c2d697..013ebff 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -88,6 +88,22 @@ static const struct hvs_format { + .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, + .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true, + }, ++ { ++ .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565, ++ .pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false, ++ }, ++ { ++ .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565, ++ .pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false, ++ }, ++ { ++ .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, ++ .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true, ++ }, ++ { ++ .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, ++ .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false, ++ }, + }; + + static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) +-- +2.7.3 + +From 149a88adaedd0bea6c6f2f12dcf893d740be2ebb Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 30 Dec 2015 12:25:44 -0800 +Subject: [PATCH 15/36] drm/vc4: Add support for YUV planes. + +This supports 420 and 422 subsampling with 2 or 3 planes, tested with +modetest. It doesn't set up chroma subsampling position (which it +appears KMS doesn't deal with yet). + +The LBM memory is overallocated in many cases, but apparently the docs +aren't quite correct and I'll probably need to look at the hardware +source to really figure it out. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_plane.c | 256 +++++++++++++++++++++++++++++++--------- + drivers/gpu/drm/vc4/vc4_regs.h | 56 ++++++++- + 2 files changed, 253 insertions(+), 59 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index 013ebff..7b0c72a 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -54,15 +54,19 @@ struct vc4_plane_state { + /* Clipped coordinates of the plane on the display. */ + int crtc_x, crtc_y, crtc_w, crtc_h; + /* Clipped area being scanned from in the FB. */ +- u32 src_x, src_y, src_w, src_h; ++ u32 src_x, src_y; + +- enum vc4_scaling_mode x_scaling, y_scaling; ++ u32 src_w[2], src_h[2]; ++ ++ /* Scaling selection for the RGB/Y plane and the Cb/Cr planes. */ ++ enum vc4_scaling_mode x_scaling[2], y_scaling[2]; + bool is_unity; ++ bool is_yuv; + + /* Offset to start scanning out from the start of the plane's + * BO. + */ +- u32 offset; ++ u32 offsets[3]; + + /* Our allocation in LBM for temporary storage during scaling. */ + struct drm_mm_node lbm; +@@ -79,6 +83,7 @@ static const struct hvs_format { + u32 hvs; /* HVS_FORMAT_* */ + u32 pixel_order; + bool has_alpha; ++ bool flip_cbcr; + } hvs_formats[] = { + { + .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, +@@ -104,6 +109,32 @@ static const struct hvs_format { + .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, + .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false, + }, ++ { ++ .drm = DRM_FORMAT_YUV422, ++ .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, ++ }, ++ { ++ .drm = DRM_FORMAT_YVU422, ++ .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, ++ .flip_cbcr = true, ++ }, ++ { ++ .drm = DRM_FORMAT_YUV420, ++ .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, ++ }, ++ { ++ .drm = DRM_FORMAT_YVU420, ++ .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, ++ .flip_cbcr = true, ++ }, ++ { ++ .drm = DRM_FORMAT_NV12, ++ .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE, ++ }, ++ { ++ .drm = DRM_FORMAT_NV16, ++ .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, ++ }, + }; + + static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) +@@ -219,11 +250,11 @@ static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) + * + * This is a replication of a table from the spec. + */ +-static u32 vc4_get_scl_field(struct drm_plane_state *state) ++static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane) + { + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + +- switch (vc4_state->x_scaling << 2 | vc4_state->y_scaling) { ++ switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) { + case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF: + return SCALER_CTL0_SCL_H_PPF_V_PPF; + case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF: +@@ -254,9 +285,16 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + struct drm_plane *plane = state->plane; + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + struct drm_framebuffer *fb = state->fb; ++ struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); + u32 subpixel_src_mask = (1 << 16) - 1; ++ u32 format = fb->pixel_format; ++ int num_planes = drm_format_num_planes(format); ++ u32 h_subsample = 1; ++ u32 v_subsample = 1; ++ int i; + +- vc4_state->offset = fb->offsets[0]; ++ for (i = 0; i < num_planes; i++) ++ vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; + + /* We don't support subpixel source positioning for scaling. */ + if ((state->src_x & subpixel_src_mask) || +@@ -268,20 +306,48 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + + vc4_state->src_x = state->src_x >> 16; + vc4_state->src_y = state->src_y >> 16; +- vc4_state->src_w = state->src_w >> 16; +- vc4_state->src_h = state->src_h >> 16; ++ vc4_state->src_w[0] = state->src_w >> 16; ++ vc4_state->src_h[0] = state->src_h >> 16; + + vc4_state->crtc_x = state->crtc_x; + vc4_state->crtc_y = state->crtc_y; + vc4_state->crtc_w = state->crtc_w; + vc4_state->crtc_h = state->crtc_h; + +- vc4_state->x_scaling = vc4_get_scaling_mode(vc4_state->src_w, +- vc4_state->crtc_w); +- vc4_state->y_scaling = vc4_get_scaling_mode(vc4_state->src_h, +- vc4_state->crtc_h); +- vc4_state->is_unity = (vc4_state->x_scaling == VC4_SCALING_NONE && +- vc4_state->y_scaling == VC4_SCALING_NONE); ++ vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], ++ vc4_state->crtc_w); ++ vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], ++ vc4_state->crtc_h); ++ ++ if (num_planes > 1) { ++ vc4_state->is_yuv = true; ++ ++ h_subsample = drm_format_horz_chroma_subsampling(format); ++ v_subsample = drm_format_vert_chroma_subsampling(format); ++ vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample; ++ vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample; ++ ++ vc4_state->x_scaling[1] = ++ vc4_get_scaling_mode(vc4_state->src_w[1], ++ vc4_state->crtc_w); ++ vc4_state->y_scaling[1] = ++ vc4_get_scaling_mode(vc4_state->src_h[1], ++ vc4_state->crtc_h); ++ ++ /* YUV conversion requires that scaling be enabled, ++ * even on a plane that's otherwise 1:1. Choose TPZ ++ * for simplicity. ++ */ ++ if (vc4_state->x_scaling[0] == VC4_SCALING_NONE) ++ vc4_state->x_scaling[0] = VC4_SCALING_TPZ; ++ if (vc4_state->y_scaling[0] == VC4_SCALING_NONE) ++ vc4_state->y_scaling[0] = VC4_SCALING_TPZ; ++ } ++ ++ vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE && ++ vc4_state->y_scaling[0] == VC4_SCALING_NONE && ++ vc4_state->x_scaling[1] == VC4_SCALING_NONE && ++ vc4_state->y_scaling[1] == VC4_SCALING_NONE); + + /* No configuring scaling on the cursor plane, since it gets + non-vblank-synced updates, and scaling requires requires +@@ -294,16 +360,27 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) + * support negative y, and negative x wastes bandwidth. + */ + if (vc4_state->crtc_x < 0) { +- vc4_state->offset += (drm_format_plane_cpp(fb->pixel_format, +- 0) * +- -vc4_state->crtc_x); +- vc4_state->src_w += vc4_state->crtc_x; ++ for (i = 0; i < num_planes; i++) { ++ u32 cpp = drm_format_plane_cpp(fb->pixel_format, i); ++ u32 subs = ((i == 0) ? 1 : h_subsample); ++ ++ vc4_state->offsets[i] += (cpp * ++ (-vc4_state->crtc_x) / subs); ++ } ++ vc4_state->src_w[0] += vc4_state->crtc_x; ++ vc4_state->src_w[1] += vc4_state->crtc_x / h_subsample; + vc4_state->crtc_x = 0; + } + + if (vc4_state->crtc_y < 0) { +- vc4_state->offset += fb->pitches[0] * -vc4_state->crtc_y; +- vc4_state->src_h += vc4_state->crtc_y; ++ for (i = 0; i < num_planes; i++) { ++ u32 subs = ((i == 0) ? 1 : v_subsample); ++ ++ vc4_state->offsets[i] += (fb->pitches[i] * ++ (-vc4_state->crtc_y) / subs); ++ } ++ vc4_state->src_h[0] += vc4_state->crtc_y; ++ vc4_state->src_h[1] += vc4_state->crtc_y / v_subsample; + vc4_state->crtc_y = 0; + } + +@@ -344,15 +421,23 @@ static u32 vc4_lbm_size(struct drm_plane_state *state) + /* This is the worst case number. One of the two sizes will + * be used depending on the scaling configuration. + */ +- u32 pix_per_line = max(vc4_state->src_w, (u32)vc4_state->crtc_w); ++ u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w); + u32 lbm; + +- if (vc4_state->is_unity) +- return 0; +- else if (vc4_state->y_scaling == VC4_SCALING_TPZ) +- lbm = pix_per_line * 8; +- else { +- /* In special cases, this multiplier might be 12. */ ++ if (!vc4_state->is_yuv) { ++ if (vc4_state->is_unity) ++ return 0; ++ else if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) ++ lbm = pix_per_line * 8; ++ else { ++ /* In special cases, this multiplier might be 12. */ ++ lbm = pix_per_line * 16; ++ } ++ } else { ++ /* There are cases for this going down to a multiplier ++ * of 2, but according to the firmware source, the ++ * table in the docs is somewhat wrong. ++ */ + lbm = pix_per_line * 16; + } + +@@ -361,33 +446,34 @@ static u32 vc4_lbm_size(struct drm_plane_state *state) + return lbm; + } + +-static void vc4_write_scaling_parameters(struct drm_plane_state *state) ++static void vc4_write_scaling_parameters(struct drm_plane_state *state, ++ int channel) + { + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + + /* Ch0 H-PPF Word 0: Scaling Parameters */ +- if (vc4_state->x_scaling == VC4_SCALING_PPF) { ++ if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) { + vc4_write_ppf(vc4_state, +- vc4_state->src_w, vc4_state->crtc_w); ++ vc4_state->src_w[channel], vc4_state->crtc_w); + } + + /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */ +- if (vc4_state->y_scaling == VC4_SCALING_PPF) { ++ if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) { + vc4_write_ppf(vc4_state, +- vc4_state->src_h, vc4_state->crtc_h); ++ vc4_state->src_h[channel], vc4_state->crtc_h); + vc4_dlist_write(vc4_state, 0xc0c0c0c0); + } + + /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */ +- if (vc4_state->x_scaling == VC4_SCALING_TPZ) { ++ if (vc4_state->x_scaling[channel] == VC4_SCALING_TPZ) { + vc4_write_tpz(vc4_state, +- vc4_state->src_w, vc4_state->crtc_w); ++ vc4_state->src_w[channel], vc4_state->crtc_w); + } + + /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */ +- if (vc4_state->y_scaling == VC4_SCALING_TPZ) { ++ if (vc4_state->y_scaling[channel] == VC4_SCALING_TPZ) { + vc4_write_tpz(vc4_state, +- vc4_state->src_h, vc4_state->crtc_h); ++ vc4_state->src_h[channel], vc4_state->crtc_h); + vc4_dlist_write(vc4_state, 0xc0c0c0c0); + } + } +@@ -401,13 +487,13 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + struct vc4_dev *vc4 = to_vc4_dev(plane->dev); + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + struct drm_framebuffer *fb = state->fb; +- struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); + u32 ctl0_offset = vc4_state->dlist_count; + const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format); +- u32 scl; ++ int num_planes = drm_format_num_planes(format->drm); ++ u32 scl0, scl1; + u32 lbm_size; + unsigned long irqflags; +- int ret; ++ int ret, i; + + ret = vc4_plane_setup_clipping_and_scaling(state); + if (ret) +@@ -432,7 +518,19 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + if (ret) + return ret; + +- scl = vc4_get_scl_field(state); ++ /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB ++ * and 4:4:4, scl1 should be set to scl0 so both channels of ++ * the scaler do the same thing. For YUV, the Y plane needs ++ * to be put in channel 1 and Cb/Cr in channel 0, so we swap ++ * the scl fields here. ++ */ ++ if (num_planes == 1) { ++ scl0 = vc4_get_scl_field(state, 1); ++ scl1 = scl0; ++ } else { ++ scl0 = vc4_get_scl_field(state, 1); ++ scl1 = vc4_get_scl_field(state, 0); ++ } + + /* Control word */ + vc4_dlist_write(vc4_state, +@@ -440,8 +538,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | + (format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | + (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) | +- VC4_SET_FIELD(scl, SCALER_CTL0_SCL0) | +- VC4_SET_FIELD(scl, SCALER_CTL0_SCL1)); ++ VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) | ++ VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1)); + + /* Position Word 0: Image Positions and Alpha Value */ + vc4_state->pos0_offset = vc4_state->dlist_count; +@@ -466,35 +564,68 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + SCALER_POS2_ALPHA_MODE_PIPELINE : + SCALER_POS2_ALPHA_MODE_FIXED, + SCALER_POS2_ALPHA_MODE) | +- VC4_SET_FIELD(vc4_state->src_w, SCALER_POS2_WIDTH) | +- VC4_SET_FIELD(vc4_state->src_h, SCALER_POS2_HEIGHT)); ++ VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) | ++ VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT)); + + /* Position Word 3: Context. Written by the HVS. */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); + +- /* Pointer Word 0: RGB / Y Pointer */ ++ ++ /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers ++ * ++ * The pointers may be any byte address. ++ */ + vc4_state->ptr0_offset = vc4_state->dlist_count; +- vc4_dlist_write(vc4_state, bo->paddr + vc4_state->offset); ++ if (!format->flip_cbcr) { ++ for (i = 0; i < num_planes; i++) ++ vc4_dlist_write(vc4_state, vc4_state->offsets[i]); ++ } else { ++ WARN_ON_ONCE(num_planes != 3); ++ vc4_dlist_write(vc4_state, vc4_state->offsets[0]); ++ vc4_dlist_write(vc4_state, vc4_state->offsets[2]); ++ vc4_dlist_write(vc4_state, vc4_state->offsets[1]); ++ } + +- /* Pointer Context Word 0: Written by the HVS */ +- vc4_dlist_write(vc4_state, 0xc0c0c0c0); ++ /* Pointer Context Word 0/1/2: Written by the HVS */ ++ for (i = 0; i < num_planes; i++) ++ vc4_dlist_write(vc4_state, 0xc0c0c0c0); + +- /* Pitch word 0: Pointer 0 Pitch */ +- vc4_dlist_write(vc4_state, +- VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH)); ++ /* Pitch word 0/1/2 */ ++ for (i = 0; i < num_planes; i++) { ++ vc4_dlist_write(vc4_state, ++ VC4_SET_FIELD(fb->pitches[i], SCALER_SRC_PITCH)); ++ } ++ ++ /* Colorspace conversion words */ ++ if (vc4_state->is_yuv) { ++ vc4_dlist_write(vc4_state, SCALER_CSC0_ITR_R_601_5); ++ vc4_dlist_write(vc4_state, SCALER_CSC1_ITR_R_601_5); ++ vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5); ++ } + + if (!vc4_state->is_unity) { + /* LBM Base Address. */ +- if (vc4_state->y_scaling != VC4_SCALING_NONE) ++ if (vc4_state->y_scaling[0] != VC4_SCALING_NONE || ++ vc4_state->y_scaling[1] != VC4_SCALING_NONE) { + vc4_dlist_write(vc4_state, vc4_state->lbm.start); ++ } + +- vc4_write_scaling_parameters(state); ++ if (num_planes > 1) { ++ /* Emit Cb/Cr as channel 0 and Y as channel ++ * 1. This matches how we set up scl0/scl1 ++ * above. ++ */ ++ vc4_write_scaling_parameters(state, 1); ++ } ++ vc4_write_scaling_parameters(state, 0); + + /* If any PPF setup was done, then all the kernel + * pointers get uploaded. + */ +- if (vc4_state->x_scaling == VC4_SCALING_PPF || +- vc4_state->y_scaling == VC4_SCALING_PPF) { ++ if (vc4_state->x_scaling[0] == VC4_SCALING_PPF || ++ vc4_state->y_scaling[0] == VC4_SCALING_PPF || ++ vc4_state->x_scaling[1] == VC4_SCALING_PPF || ++ vc4_state->y_scaling[1] == VC4_SCALING_PPF) { + u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start, + SCALER_PPF_KERNEL_OFFSET); + +@@ -698,6 +829,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, + struct drm_plane *plane = NULL; + struct vc4_plane *vc4_plane; + u32 formats[ARRAY_SIZE(hvs_formats)]; ++ u32 num_formats = 0; + int ret = 0; + unsigned i; + +@@ -708,12 +840,20 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, + goto fail; + } + +- for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) +- formats[i] = hvs_formats[i].drm; ++ for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { ++ /* Don't allow YUV in cursor planes, since that means ++ * tuning on the scaler, which we don't allow for the ++ * cursor. ++ */ ++ if (type != DRM_PLANE_TYPE_CURSOR || ++ hvs_formats[i].hvs < HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE) { ++ formats[num_formats++] = hvs_formats[i].drm; ++ } ++ } + plane = &vc4_plane->base; + ret = drm_universal_plane_init(dev, plane, 0xff, + &vc4_plane_funcs, +- formats, ARRAY_SIZE(formats), ++ formats, num_formats, + type, NULL); + + drm_plane_helper_add(plane, &vc4_plane_helper_funcs); +diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h +index a5b544d..bf42a8e 100644 +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -519,7 +519,12 @@ enum hvs_pixel_format { + HVS_PIXEL_FORMAT_RGB888 = 5, + HVS_PIXEL_FORMAT_RGBA6666 = 6, + /* 32bpp */ +- HVS_PIXEL_FORMAT_RGBA8888 = 7 ++ HVS_PIXEL_FORMAT_RGBA8888 = 7, ++ ++ HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE = 8, ++ HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE = 9, ++ HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE = 10, ++ HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE = 11, + }; + + /* Note: the LSB is the rightmost character shown. Only valid for +@@ -601,6 +606,55 @@ enum hvs_pixel_format { + #define SCALER_POS2_WIDTH_MASK VC4_MASK(11, 0) + #define SCALER_POS2_WIDTH_SHIFT 0 + ++/* Color Space Conversion words. Some values are S2.8 signed ++ * integers, except that the 2 integer bits map as {0x0: 0, 0x1: 1, ++ * 0x2: 2, 0x3: -1} ++ */ ++/* bottom 8 bits of S2.8 contribution of Cr to Blue */ ++#define SCALER_CSC0_COEF_CR_BLU_MASK VC4_MASK(31, 24) ++#define SCALER_CSC0_COEF_CR_BLU_SHIFT 24 ++/* Signed offset to apply to Y before CSC. (Y' = Y + YY_OFS) */ ++#define SCALER_CSC0_COEF_YY_OFS_MASK VC4_MASK(23, 16) ++#define SCALER_CSC0_COEF_YY_OFS_SHIFT 16 ++/* Signed offset to apply to CB before CSC (Cb' = Cb - 128 + CB_OFS). */ ++#define SCALER_CSC0_COEF_CB_OFS_MASK VC4_MASK(15, 8) ++#define SCALER_CSC0_COEF_CB_OFS_SHIFT 8 ++/* Signed offset to apply to CB before CSC (Cr' = Cr - 128 + CR_OFS). */ ++#define SCALER_CSC0_COEF_CR_OFS_MASK VC4_MASK(7, 0) ++#define SCALER_CSC0_COEF_CR_OFS_SHIFT 0 ++#define SCALER_CSC0_ITR_R_601_5 0x00f00000 ++#define SCALER_CSC0_ITR_R_709_3 0x00f00000 ++#define SCALER_CSC0_JPEG_JFIF 0x00000000 ++ ++/* S2.8 contribution of Cb to Green */ ++#define SCALER_CSC1_COEF_CB_GRN_MASK VC4_MASK(31, 22) ++#define SCALER_CSC1_COEF_CB_GRN_SHIFT 22 ++/* S2.8 contribution of Cr to Green */ ++#define SCALER_CSC1_COEF_CR_GRN_MASK VC4_MASK(21, 12) ++#define SCALER_CSC1_COEF_CR_GRN_SHIFT 12 ++/* S2.8 contribution of Y to all of RGB */ ++#define SCALER_CSC1_COEF_YY_ALL_MASK VC4_MASK(11, 2) ++#define SCALER_CSC1_COEF_YY_ALL_SHIFT 2 ++/* top 2 bits of S2.8 contribution of Cr to Blue */ ++#define SCALER_CSC1_COEF_CR_BLU_MASK VC4_MASK(1, 0) ++#define SCALER_CSC1_COEF_CR_BLU_SHIFT 0 ++#define SCALER_CSC1_ITR_R_601_5 0xe73304a8 ++#define SCALER_CSC1_ITR_R_709_3 0xf2b784a8 ++#define SCALER_CSC1_JPEG_JFIF 0xea34a400 ++ ++/* S2.8 contribution of Cb to Red */ ++#define SCALER_CSC2_COEF_CB_RED_MASK VC4_MASK(29, 20) ++#define SCALER_CSC2_COEF_CB_RED_SHIFT 20 ++/* S2.8 contribution of Cr to Red */ ++#define SCALER_CSC2_COEF_CR_RED_MASK VC4_MASK(19, 10) ++#define SCALER_CSC2_COEF_CR_RED_SHIFT 10 ++/* S2.8 contribution of Cb to Blue */ ++#define SCALER_CSC2_COEF_CB_BLU_MASK VC4_MASK(19, 10) ++#define SCALER_CSC2_COEF_CB_BLU_SHIFT 10 ++#define SCALER_CSC2_ITR_R_601_5 0x00066204 ++#define SCALER_CSC2_ITR_R_709_3 0x00072a1c ++#define SCALER_CSC2_JPEG_JFIF 0x000599c5 ++ + #define SCALER_TPZ0_VERT_RECALC BIT(31) + #define SCALER_TPZ0_SCALE_MASK VC4_MASK(28, 8) + #define SCALER_TPZ0_SCALE_SHIFT 8 +-- +2.7.3 + +From 47ed1ee3dbfde89297b81bc09f1b483f4da1b06d Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 29 Feb 2016 17:53:00 -0800 +Subject: [PATCH 16/36] drm/vc4: Let gpiolib know that we're OK with sleeping + for HPD. + +Fixes an error thrown every few seconds when we poll HPD when it's on +a I2C to GPIO expander. + +Signed-off-by: Eric Anholt +Tested-by: Daniel Stone +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c +index 56272ca..6bcf51d 100644 +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -166,7 +166,7 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) + struct vc4_dev *vc4 = to_vc4_dev(dev); + + if (vc4->hdmi->hpd_gpio) { +- if (gpio_get_value(vc4->hdmi->hpd_gpio)) ++ if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio)) + return connector_status_connected; + else + return connector_status_disconnected; +-- +2.7.3 + +From 24b28acef42486c282bc58e977cbfc66191a8f38 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 29 Feb 2016 17:53:01 -0800 +Subject: [PATCH 17/36] drm/vc4: Respect GPIO_ACTIVE_LOW on HDMI HPD if set in + the devicetree. + +The original Raspberry Pi had the GPIO active high, but the later +models are active low. The DT GPIO bindings allow specifying the +active flag, except that it doesn't get propagated to the gpiodesc, so +you have to handle it yourself. + +Signed-off-by: Eric Anholt +Tested-by: Daniel Stone +--- + drivers/gpu/drm/vc4/vc4_hdmi.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c +index 6bcf51d..d8b8649 100644 +--- a/drivers/gpu/drm/vc4/vc4_hdmi.c ++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c +@@ -47,6 +47,7 @@ struct vc4_hdmi { + void __iomem *hdmicore_regs; + void __iomem *hd_regs; + int hpd_gpio; ++ bool hpd_active_low; + + struct clk *pixel_clock; + struct clk *hsm_clock; +@@ -166,7 +167,8 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) + struct vc4_dev *vc4 = to_vc4_dev(dev); + + if (vc4->hdmi->hpd_gpio) { +- if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio)) ++ if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^ ++ vc4->hdmi->hpd_active_low) + return connector_status_connected; + else + return connector_status_disconnected; +@@ -517,11 +519,17 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) + * we'll use the HDMI core's register. + */ + if (of_find_property(dev->of_node, "hpd-gpios", &value)) { +- hdmi->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpios", 0); ++ enum of_gpio_flags hpd_gpio_flags; ++ ++ hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node, ++ "hpd-gpios", 0, ++ &hpd_gpio_flags); + if (hdmi->hpd_gpio < 0) { + ret = hdmi->hpd_gpio; + goto err_unprepare_hsm; + } ++ ++ hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW; + } + + vc4->hdmi = hdmi; +-- +2.7.3 + +From ed6836e411dd559a811dd063509a01772f4fe00f Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 4 Mar 2016 12:32:07 -0800 +Subject: [PATCH 19/36] drm/vc4: Recognize a more specific compatible string + for V3D. + +The Raspberry Pi Foundation's firmware updates are shipping device +trees using the old string, so we'll keep recognizing that as this rev +of V3D. Still, we should use a more specific name in the upstream DT +to clarify which board is being supported, in case we do other revs of +V3D in the future. + +Signed-off-by: Eric Anholt +Acked-by: Stephen Warren +--- + drivers/gpu/drm/vc4/vc4_v3d.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c +index 31de5d1..e6d3c60 100644 +--- a/drivers/gpu/drm/vc4/vc4_v3d.c ++++ b/drivers/gpu/drm/vc4/vc4_v3d.c +@@ -268,6 +268,7 @@ static int vc4_v3d_dev_remove(struct platform_device *pdev) + } + + static const struct of_device_id vc4_v3d_dt_match[] = { ++ { .compatible = "brcm,bcm2835-v3d" }, + { .compatible = "brcm,vc4-v3d" }, + {} + }; +-- +2.7.3 + +From 55acd7db60c8247d926969b705373765c26c1f44 Mon Sep 17 00:00:00 2001 +From: Martin Sperl +Date: Fri, 11 Sep 2015 11:22:05 +0000 +Subject: [PATCH 21/36] ARM: bcm2835: add the auxiliary spi1 and spi2 to the + device tree + +This enables the use of the auxiliary spi1 and spi2 devices +on the bcm2835 SOC. + +Note that this requires the use of the new clk-bcm2835-aux to work. + +Signed-off-by: Martin Sperl +Acked-by: Stephen Warren +[anholt: Rebased on 2835.dtsi -> 283x.dtsi change] +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/bcm283x.dtsi | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +index 971e741..f0d4573 100644 +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -1,5 +1,6 @@ + #include + #include ++#include + #include "skeleton.dtsi" + + /* This include file covers the common peripherals and configuration between +@@ -159,6 +160,26 @@ + clocks = <&clocks BCM2835_CLOCK_VPU>; + }; + ++ spi1: spi@7e215080 { ++ compatible = "brcm,bcm2835-aux-spi"; ++ reg = <0x7e215080 0x40>; ++ interrupts = <1 29>; ++ clocks = <&aux BCM2835_AUX_CLOCK_SPI1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ spi2: spi@7e2150c0 { ++ compatible = "brcm,bcm2835-aux-spi"; ++ reg = <0x7e2150c0 0x40>; ++ interrupts = <1 29>; ++ clocks = <&aux BCM2835_AUX_CLOCK_SPI2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ + sdhci: sdhci@7e300000 { + compatible = "brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; +-- +2.7.3 + +From 3e0a385cebe3b0d9338cab356c6a11daaa64f808 Mon Sep 17 00:00:00 2001 +From: Remi Pommarel +Date: Mon, 21 Dec 2015 21:12:59 +0100 +Subject: [PATCH 22/36] ARM: bcm2835: Add PWM clock support to the device tree + +Signed-off-by: Remi Pommarel +[anholt: Rebased on 2835.dtsi -> 283x.dtsi change] +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/bcm2835-rpi.dtsi | 4 ++++ + arch/arm/boot/dts/bcm283x.dtsi | 10 ++++++++++ + 2 files changed, 14 insertions(+) + +diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi +index 3afb9fe..a584a93 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi +@@ -58,3 +58,7 @@ + status = "okay"; + bus-width = <4>; + }; ++ ++&pwm { ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +index f0d4573..e4a2792 100644 +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -180,6 +180,16 @@ + status = "disabled"; + }; + ++ pwm: pwm@7e20c000 { ++ compatible = "brcm,bcm2835-pwm"; ++ reg = <0x7e20c000 0x28>; ++ clocks = <&clocks BCM2835_CLOCK_PWM>; ++ assigned-clocks = <&clocks BCM2835_CLOCK_PWM>; ++ assigned-clock-rates = <10000000>; ++ #pwm-cells = <2>; ++ status = "disabled"; ++ }; ++ + sdhci: sdhci@7e300000 { + compatible = "brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; +-- +2.7.3 + +From 84416a8360e7c31e6ba9f7775c077bd5f3fe32de Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Mon, 25 Jan 2016 21:40:06 +0100 +Subject: [PATCH 23/36] ARM: bcm2835: dt: Add Raspberry Pi Model A + +This one is essentially the same as revision 2 B board (with the I2S on +P5 header). + +Signed-off-by: Lubomir Rintel +Acked-by: Stephen Warren +[anholt: Rebased on bcm2835.dtsi -> bcm283x.dtsi change] +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/bcm2835-rpi-a.dts | 24 ++++++++++++++++++++++++ + 2 files changed, 25 insertions(+) + create mode 100644 arch/arm/boot/dts/bcm2835-rpi-a.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index a4a6d70..d000814 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -60,6 +60,7 @@ dtb-$(CONFIG_ARCH_AXXIA) += \ + axm5516-amarillo.dtb + dtb-$(CONFIG_ARCH_BCM2835) += \ + bcm2835-rpi-b.dtb \ ++ bcm2835-rpi-a.dtb \ + bcm2835-rpi-b-rev2.dtb \ + bcm2835-rpi-b-plus.dtb \ + bcm2835-rpi-a-plus.dtb \ +diff --git a/arch/arm/boot/dts/bcm2835-rpi-a.dts b/arch/arm/boot/dts/bcm2835-rpi-a.dts +new file mode 100644 +index 0000000..ddbbbbd +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2835-rpi-a.dts +@@ -0,0 +1,24 @@ ++/dts-v1/; ++#include "bcm2835.dtsi" ++#include "bcm2835-rpi.dtsi" ++ ++/ { ++ compatible = "raspberrypi,model-a", "brcm,bcm2835"; ++ model = "Raspberry Pi Model A"; ++ ++ leds { ++ act { ++ gpios = <&gpio 16 1>; ++ }; ++ }; ++}; ++ ++&gpio { ++ pinctrl-0 = <&gpioout &alt0 &i2s_alt2 &alt3>; ++ ++ /* I2S interface */ ++ i2s_alt2: i2s_alt2 { ++ brcm,pins = <28 29 30 31>; ++ brcm,function = ; ++ }; ++}; +-- +2.7.3 + +From f6fd06c97f0d6d8398b8caba1b6879fa7e0284ba Mon Sep 17 00:00:00 2001 +From: Alexander Aring +Date: Wed, 16 Dec 2015 16:26:49 -0800 +Subject: [PATCH 24/36] ARM: bcm2835: Add the Raspberry Pi power domain driver + to the DT. + +This connects the USB driver to the USB power domain, so that USB can +actually be turned on at boot if the bootloader didn't do it for us. + +Signed-off-by: Alexander Aring +Signed-off-by: Eric Anholt +Reviewed-by: Kevin Hilman +--- + arch/arm/boot/dts/bcm2835-rpi.dtsi | 12 ++++++++++++ + arch/arm/boot/dts/bcm283x.dtsi | 2 +- + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi +index a584a93..76bdbca 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi +@@ -1,3 +1,5 @@ ++#include ++ + / { + memory { + reg = <0 0x10000000>; +@@ -18,6 +20,12 @@ + compatible = "raspberrypi,bcm2835-firmware"; + mboxes = <&mailbox>; + }; ++ ++ power: power { ++ compatible = "raspberrypi,bcm2835-power"; ++ firmware = <&firmware>; ++ #power-domain-cells = <1>; ++ }; + }; + }; + +@@ -62,3 +70,7 @@ + &pwm { + status = "okay"; + }; ++ ++&usb { ++ power-domains = <&power RPI_POWER_DOMAIN_USB>; ++}; +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +index e4a2792..e69a6cf 100644 +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -218,7 +218,7 @@ + status = "disabled"; + }; + +- usb@7e980000 { ++ usb: usb@7e980000 { + compatible = "brcm,bcm2835-usb"; + reg = <0x7e980000 0x10000>; + interrupts = <1 9>; +-- +2.7.3 + +From 580146c680ac7b706ac54d7d7db8204bee3d2e93 Mon Sep 17 00:00:00 2001 +From: Martin Sperl +Date: Fri, 12 Feb 2016 11:14:25 +0000 +Subject: [PATCH 25/36] ARM: bcm2835: add bcm2835-aux-uart support to DT + +Add bcm2835-aux-uart support to the device tree. + +Signed-off-by: Martin Sperl +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/bcm283x.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +index e69a6cf..fc67964 100644 +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -160,6 +160,14 @@ + clocks = <&clocks BCM2835_CLOCK_VPU>; + }; + ++ uart1: serial@7e215040 { ++ compatible = "brcm,bcm2835-aux-uart"; ++ reg = <0x7e215040 0x40>; ++ interrupts = <1 29>; ++ clocks = <&aux BCM2835_AUX_CLOCK_UART>; ++ status = "disabled"; ++ }; ++ + spi1: spi@7e215080 { + compatible = "brcm,bcm2835-aux-spi"; + reg = <0x7e215080 0x40>; +-- +2.7.3 + +From 41135d2ce60509e53306e5b76afab98ddc15951b Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 2 Mar 2015 14:36:16 -0800 +Subject: [PATCH 26/36] ARM: bcm2835: Add VC4 to the device tree. + +VC4 is the GPU (display and 3D) present on the 283x. + +v2: Sort by register address, mark HDMI as disabled by default in the + SoC file and enable it from -rpi. +v3: Add references to the pixel/HSM clocks for HDMI. Rename + compatibility strings and clean up node names. +v4: Fix comment marking pv0's interrupt as pwa2 instead of pwa0. + Rename hpd-gpio to hpd-gpios. +v5: Rebase on bcm283x.dtsi change, add v3d. +v6: Make HDMI reference the power domain. +v7: Fix the HDMI HPD gpios active value and HDMI enable for each RPI + board. Change V3D compatible string to 2835. + +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/bcm2835-rpi-a-plus.dts | 4 +++ + arch/arm/boot/dts/bcm2835-rpi-a.dts | 4 +++ + arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 4 +++ + arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts | 4 +++ + arch/arm/boot/dts/bcm2835-rpi-b.dts | 4 +++ + arch/arm/boot/dts/bcm2835-rpi.dtsi | 9 ++++++ + arch/arm/boot/dts/bcm2836-rpi-2-b.dts | 4 +++ + arch/arm/boot/dts/bcm283x.dtsi | 47 ++++++++++++++++++++++++++++++++ + 8 files changed, 80 insertions(+) + +diff --git a/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts +index 228614f..35ff4e7a 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts +@@ -29,3 +29,7 @@ + brcm,function = ; + }; + }; ++ ++&hdmi { ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; ++}; +diff --git a/arch/arm/boot/dts/bcm2835-rpi-a.dts b/arch/arm/boot/dts/bcm2835-rpi-a.dts +index ddbbbbd..306a84e 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-a.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-a.dts +@@ -22,3 +22,7 @@ + brcm,function = ; + }; + }; ++ ++&hdmi { ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>; ++}; +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +index ef54050..57d313b 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +@@ -29,3 +29,7 @@ + brcm,function = ; + }; + }; ++ ++&hdmi { ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; ++}; +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts +index 86f1f2f..cf2774e 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts +@@ -22,3 +22,7 @@ + brcm,function = ; + }; + }; ++ ++&hdmi { ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; ++}; +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b.dts b/arch/arm/boot/dts/bcm2835-rpi-b.dts +index 4859e9d..8b15f9c 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts +@@ -16,3 +16,7 @@ + &gpio { + pinctrl-0 = <&gpioout &alt0 &alt3>; + }; ++ ++&hdmi { ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>; ++}; +diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi +index 76bdbca..caf2707 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi +@@ -74,3 +74,12 @@ + &usb { + power-domains = <&power RPI_POWER_DOMAIN_USB>; + }; ++ ++&v3d { ++ power-domains = <&power RPI_POWER_DOMAIN_V3D>; ++}; ++ ++&hdmi { ++ power-domains = <&power RPI_POWER_DOMAIN_HDMI>; ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +index ff94666..c4743f4 100644 +--- a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +@@ -33,3 +33,7 @@ + brcm,function = ; + }; + }; ++ ++&hdmi { ++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; ++}; +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +index fc67964..bbe4eab 100644 +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -1,6 +1,7 @@ + #include + #include + #include ++#include + #include "skeleton.dtsi" + + /* This include file covers the common peripherals and configuration between +@@ -153,6 +154,18 @@ + status = "disabled"; + }; + ++ pixelvalve@7e206000 { ++ compatible = "brcm,bcm2835-pixelvalve0"; ++ reg = <0x7e206000 0x100>; ++ interrupts = <2 13>; /* pwa0 */ ++ }; ++ ++ pixelvalve@7e207000 { ++ compatible = "brcm,bcm2835-pixelvalve1"; ++ reg = <0x7e207000 0x100>; ++ interrupts = <2 14>; /* pwa1 */ ++ }; ++ + aux: aux@0x7e215000 { + compatible = "brcm,bcm2835-aux"; + #clock-cells = <1>; +@@ -206,6 +219,12 @@ + status = "disabled"; + }; + ++ hvs@7e400000 { ++ compatible = "brcm,bcm2835-hvs"; ++ reg = <0x7e400000 0x6000>; ++ interrupts = <2 1>; ++ }; ++ + i2c1: i2c@7e804000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e804000 0x1000>; +@@ -226,11 +245,39 @@ + status = "disabled"; + }; + ++ pixelvalve@7e807000 { ++ compatible = "brcm,bcm2835-pixelvalve2"; ++ reg = <0x7e807000 0x100>; ++ interrupts = <2 10>; /* pixelvalve */ ++ }; ++ ++ hdmi: hdmi@7e902000 { ++ compatible = "brcm,bcm2835-hdmi"; ++ reg = <0x7e902000 0x600>, ++ <0x7e808000 0x100>; ++ interrupts = <2 8>, <2 9>; ++ ddc = <&i2c2>; ++ clocks = <&clocks BCM2835_PLLH_PIX>, ++ <&clocks BCM2835_CLOCK_HSM>; ++ clock-names = "pixel", "hdmi"; ++ status = "disabled"; ++ }; ++ + usb: usb@7e980000 { + compatible = "brcm,bcm2835-usb"; + reg = <0x7e980000 0x10000>; + interrupts = <1 9>; + }; ++ ++ v3d: v3d@7ec00000 { ++ compatible = "brcm,bcm2835-v3d"; ++ reg = <0x7ec00000 0x1000>; ++ interrupts = <1 10>; ++ }; ++ ++ vc4: gpu { ++ compatible = "brcm,bcm2835-vc4"; ++ }; + }; + + clocks { +-- +2.7.3 + +From da77f737f9f5a487f3a1f80f8546585ee18cd7b9 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 4 Mar 2016 10:39:28 -0800 +Subject: [PATCH 27/36] dt-bindings: Add root properties for Raspberry Pi 3 + +Signed-off-by: Eric Anholt +Acked-by: Rob Herring +--- + Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt +index 11d3056..6ffe087 100644 +--- a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt ++++ b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt +@@ -30,6 +30,10 @@ Raspberry Pi 2 Model B + Required root node properties: + compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; + ++Raspberry Pi 3 Model B ++Required root node properties: ++compatible = "raspberrypi,3-model-b", "brcm,bcm2837"; ++ + Raspberry Pi Compute Module + Required root node properties: + compatible = "raspberrypi,compute-module", "brcm,bcm2835"; +-- +2.7.3 + +From b76b1cdf2e569cceab41dcf3b3f6a90965d0a02c Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 4 Mar 2016 10:39:29 -0800 +Subject: [PATCH 28/36] ARM: bcm2835: Add devicetree for the Raspberry Pi 3. + +For now this doesn't support the new hardware present on the Pi 3 (BT, +wifi, GPIO expander). Since the GPIO expander isn't supported, we +also don't have the LEDs like the other board files do. + +Signed-off-by: Eric Anholt +Acked-by: Stephen Warren +--- + arch/arm/boot/dts/Makefile | 3 +- + arch/arm/boot/dts/bcm2837-rpi-3-b.dts | 22 ++++++++++++ + arch/arm/boot/dts/bcm2837.dtsi | 68 +++++++++++++++++++++++++++++++++++ + 3 files changed, 92 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/boot/dts/bcm2837-rpi-3-b.dts + create mode 100644 arch/arm/boot/dts/bcm2837.dtsi + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index d000814..a8a0767 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -64,7 +64,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += \ + bcm2835-rpi-b-rev2.dtb \ + bcm2835-rpi-b-plus.dtb \ + bcm2835-rpi-a-plus.dtb \ +- bcm2836-rpi-2-b.dtb ++ bcm2836-rpi-2-b.dtb \ ++ bcm2837-rpi-3-b.dtb + dtb-$(CONFIG_ARCH_BCM_5301X) += \ + bcm4708-asus-rt-ac56u.dtb \ + bcm4708-asus-rt-ac68u.dtb \ +diff --git a/arch/arm/boot/dts/bcm2837-rpi-3-b.dts b/arch/arm/boot/dts/bcm2837-rpi-3-b.dts +new file mode 100644 +index 0000000..5e8eafd +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2837-rpi-3-b.dts +@@ -0,0 +1,22 @@ ++/dts-v1/; ++#include "bcm2837.dtsi" ++#include "bcm2835-rpi.dtsi" ++ ++/ { ++ compatible = "raspberrypi,3-model-b", "brcm,bcm2837"; ++ model = "Raspberry Pi 3 Model B"; ++ ++ memory { ++ reg = <0 0x40000000>; ++ }; ++}; ++ ++&gpio { ++ pinctrl-0 = <&gpioout &alt0 &i2s_alt0 &alt3>; ++ ++ /* I2S interface */ ++ i2s_alt0: i2s_alt0 { ++ brcm,pins = <28 29 30 31>; ++ brcm,function = ; ++ }; ++}; +diff --git a/arch/arm/boot/dts/bcm2837.dtsi b/arch/arm/boot/dts/bcm2837.dtsi +new file mode 100644 +index 0000000..2f36722 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2837.dtsi +@@ -0,0 +1,68 @@ ++#include "bcm283x.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2836"; ++ ++ soc { ++ ranges = <0x7e000000 0x3f000000 0x1000000>, ++ <0x40000000 0x40000000 0x00001000>; ++ dma-ranges = <0xc0000000 0x00000000 0x3f000000>; ++ ++ local_intc: local_intc { ++ compatible = "brcm,bcm2836-l1-intc"; ++ reg = <0x40000000 0x100>; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ interrupt-parent = <&local_intc>; ++ }; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupt-parent = <&local_intc>; ++ interrupts = <0>, // PHYS_SECURE_PPI ++ <1>, // PHYS_NONSECURE_PPI ++ <3>, // VIRT_PPI ++ <2>; // HYP_PPI ++ always-on; ++ }; ++ ++ cpus: cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53"; ++ reg = <0>; ++ }; ++ ++ cpu1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53"; ++ reg = <1>; ++ }; ++ ++ cpu2: cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53"; ++ reg = <2>; ++ }; ++ ++ cpu3: cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a53"; ++ reg = <3>; ++ }; ++ }; ++}; ++ ++/* Make the BCM2835-style global interrupt controller be a child of the ++ * CPU-local interrupt controller. ++ */ ++&intc { ++ compatible = "brcm,bcm2836-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-parent = <&local_intc>; ++ interrupts = <8>; ++}; +-- +2.7.3 + +From 43aa67b7bccfb189a3e57832f08710c98fe707c6 Mon Sep 17 00:00:00 2001 +From: Martin Sperl +Date: Sun, 17 Jan 2016 12:15:28 +0000 +Subject: [PATCH 29/36] ARM: bcm2835: follow dt uart node-naming convention + +This patch fixes the naming of the device tree node: uart@7e201000 +to conform to the standard of: serial@7e201000 + +Signed-off-by: Martin Sperl +--- + arch/arm/boot/dts/bcm283x.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +index bbe4eab..31cc2f2 100644 +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -113,7 +113,7 @@ + #interrupt-cells = <2>; + }; + +- uart0: uart@7e201000 { ++ uart0: serial@7e201000 { + compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; + reg = <0x7e201000 0x1000>; + interrupts = <2 25>; +-- +2.7.3 + +From 72b53a14be5ff0bda535faefa09bc9726acbe1ff Mon Sep 17 00:00:00 2001 +From: Martin Sperl +Date: Sun, 17 Jan 2016 12:15:29 +0000 +Subject: [PATCH 30/36] dt/bindings: serial: bcm2835: add binding documentation + for bcm2835-aux-uart + +Add binding documentation for the bcm2835-aux-uart driver. + +Signed-off-by: Martin Sperl + +Changelog: + V2->V3: fixed naming convention for node +Acked-by: Rob Herring +Acked-by: Eric Anholt +--- + .../bindings/serial/brcm,bcm2835-aux-uart.txt | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + create mode 100644 Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.txt + +diff --git a/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.txt b/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.txt +new file mode 100644 +index 0000000..b5cc629 +--- /dev/null ++++ b/Documentation/devicetree/bindings/serial/brcm,bcm2835-aux-uart.txt +@@ -0,0 +1,18 @@ ++* BCM2835 AUXILIAR UART ++ ++Required properties: ++ ++- compatible: "brcm,bcm2835-aux-uart" ++- reg: The base address of the UART register bank. ++- interrupts: A single interrupt specifier. ++- clocks: Clock driving the hardware; used to figure out the baud rate ++ divisor. ++ ++Example: ++ ++ uart1: serial@7e215040 { ++ compatible = "brcm,bcm2835-aux-uart"; ++ reg = <0x7e215040 0x40>; ++ interrupts = <1 29>; ++ clocks = <&aux BCM2835_AUX_CLOCK_UART>; ++ }; +-- +2.7.3 + +From 285a4ac466d3712b50f5c0d29bf5874476f00c30 Mon Sep 17 00:00:00 2001 +From: Martin Sperl +Date: Sun, 17 Jan 2016 12:15:30 +0000 +Subject: [PATCH 31/36] serial: bcm2835: add driver for bcm2835-aux-uart + +The bcm2835 SOC contains an auxiliary uart, which is very close +to the ns16550 with some differences. + +The big difference is that the uart HW is not using an internal divider +of 16 but 8, which results in an effictive baud-rate being twice +the requested baud-rate. + +This driver handles this device correctly and handles the difference in +the HW divider by scaling up the clock by a factor of 2. + +The approach to write a separate (wrapper) driver instead of using a +multiplying clock and "ns16550" as compatibility in the device-tree +has been recommended by Stephen Warren. + +Signed-off-by: Martin Sperl + +Changelog: + V1->V2: made an explicit bcm2835-aux-uart driver + not conrolling the settings via DT only + V2->V3: added comments on UART capabilities + applied recommendations by Stefan Wahren + keep registered line-id in bcm2835aux_data +Acked-by: Eric Anholt +--- + drivers/tty/serial/8250/8250_bcm2835aux.c | 146 ++++++++++++++++++++++++++++++ + drivers/tty/serial/8250/Kconfig | 24 +++++ + drivers/tty/serial/8250/Makefile | 1 + + 3 files changed, 171 insertions(+) + create mode 100644 drivers/tty/serial/8250/8250_bcm2835aux.c + +diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c +new file mode 100644 +index 0000000..ecf89f1 +--- /dev/null ++++ b/drivers/tty/serial/8250/8250_bcm2835aux.c +@@ -0,0 +1,146 @@ ++/* ++ * Serial port driver for BCM2835AUX UART ++ * ++ * Copyright (C) 2016 Martin Sperl ++ * ++ * Based on 8250_lpc18xx.c: ++ * Copyright (C) 2015 Joachim Eastwood ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "8250.h" ++ ++struct bcm2835aux_data { ++ struct uart_8250_port uart; ++ struct clk *clk; ++ int line; ++}; ++ ++static int bcm2835aux_serial_probe(struct platform_device *pdev) ++{ ++ struct bcm2835aux_data *data; ++ struct resource *res; ++ int ret; ++ ++ /* allocate the custom structure */ ++ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ /* initialize data */ ++ spin_lock_init(&data->uart.port.lock); ++ data->uart.capabilities = UART_CAP_FIFO; ++ data->uart.port.dev = &pdev->dev; ++ data->uart.port.regshift = 2; ++ data->uart.port.type = PORT_16550; ++ data->uart.port.iotype = UPIO_MEM; ++ data->uart.port.fifosize = 8; ++ data->uart.port.flags = UPF_SHARE_IRQ | ++ UPF_FIXED_PORT | ++ UPF_FIXED_TYPE | ++ UPF_SKIP_TEST; ++ ++ /* get the clock - this also enables the HW */ ++ data->clk = devm_clk_get(&pdev->dev, NULL); ++ ret = PTR_ERR_OR_ZERO(data->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "could not get clk: %d\n", ret); ++ return ret; ++ } ++ ++ /* get the interrupt */ ++ data->uart.port.irq = platform_get_irq(pdev, 0); ++ if (data->uart.port.irq < 0) { ++ dev_err(&pdev->dev, "irq not found - %i", ++ data->uart.port.irq); ++ return data->uart.port.irq; ++ } ++ ++ /* map the main registers */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "memory resource not found"); ++ return -EINVAL; ++ } ++ data->uart.port.membase = devm_ioremap_resource(&pdev->dev, res); ++ ret = PTR_ERR_OR_ZERO(data->uart.port.membase); ++ if (ret) ++ return ret; ++ ++ /* Check for a fixed line number */ ++ ret = of_alias_get_id(pdev->dev.of_node, "serial"); ++ if (ret >= 0) ++ data->uart.port.line = ret; ++ ++ /* enable the clock as a last step */ ++ ret = clk_prepare_enable(data->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "unable to enable uart clock - %d\n", ++ ret); ++ return ret; ++ } ++ ++ /* the HW-clock divider for bcm2835aux is 8, ++ * but 8250 expects a divider of 16, ++ * so we have to multiply the actual clock by 2 ++ * to get identical baudrates. ++ */ ++ data->uart.port.uartclk = clk_get_rate(data->clk) * 2; ++ ++ /* register the port */ ++ ret = serial8250_register_8250_port(&data->uart); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "unable to register 8250 port - %d\n", ++ ret); ++ goto dis_clk; ++ } ++ data->line = ret; ++ ++ platform_set_drvdata(pdev, data); ++ ++ return 0; ++ ++dis_clk: ++ clk_disable_unprepare(data->clk); ++ return ret; ++} ++ ++static int bcm2835aux_serial_remove(struct platform_device *pdev) ++{ ++ struct bcm2835aux_data *data = platform_get_drvdata(pdev); ++ ++ serial8250_unregister_port(data->uart.port.line); ++ clk_disable_unprepare(data->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id bcm2835aux_serial_match[] = { ++ { .compatible = "brcm,bcm2835-aux-uart" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, bcm2835aux_serial_match); ++ ++static struct platform_driver bcm2835aux_serial_driver = { ++ .driver = { ++ .name = "bcm2835-aux-uart", ++ .of_match_table = bcm2835aux_serial_match, ++ }, ++ .probe = bcm2835aux_serial_probe, ++ .remove = bcm2835aux_serial_remove, ++}; ++module_platform_driver(bcm2835aux_serial_driver); ++ ++MODULE_DESCRIPTION("BCM2835 auxiliar UART driver"); ++MODULE_AUTHOR("Martin Sperl "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig +index b03cb517..67ad6b0 100644 +--- a/drivers/tty/serial/8250/Kconfig ++++ b/drivers/tty/serial/8250/Kconfig +@@ -272,6 +272,30 @@ config SERIAL_8250_ACORN + system, say Y to this option. The driver can handle 1, 2, or 3 port + cards. If unsure, say N. + ++config SERIAL_8250_BCM2835AUX ++ tristate "BCM2835 auxiliar mini UART support" ++ depends on ARCH_BCM2835 || COMPILE_TEST ++ depends on SERIAL_8250 && SERIAL_8250_SHARE_IRQ ++ help ++ Support for the BCM2835 auxiliar mini UART. ++ ++ Features and limitations of the UART are ++ Registers are similar to 16650 registers, ++ set bits in the control registers that are unsupported ++ are ignored and read back as 0 ++ 7/8 bit operation with 1 start and 1 stop bit ++ 8 symbols deep fifo for rx and tx ++ SW controlled RTS and SW readable CTS ++ Clock rate derived from system clock ++ Uses 8 times oversampling (compared to 16 times for 16650) ++ Missing break detection (but break generation) ++ Missing framing error detection ++ Missing parity bit ++ Missing receive time-out interrupt ++ Missing DCD, DSR, DTR and RI signals ++ ++ If unsure, say N. ++ + config SERIAL_8250_FSL + bool + depends on SERIAL_8250_CONSOLE +diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile +index b9b9bca..5c1869f 100644 +--- a/drivers/tty/serial/8250/Makefile ++++ b/drivers/tty/serial/8250/Makefile +@@ -12,6 +12,7 @@ obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o + obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o + obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o + obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o ++obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o + obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o + obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o + obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o +-- +2.7.3 + +From 528285e99c25249456023d28f521689bf9e9eb8b Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Wed, 30 Mar 2016 09:35:13 +0100 +Subject: [PATCH 32/36] drop usb power domain support for the moment, kills usb + +--- + arch/arm/boot/dts/bcm2835-rpi.dtsi | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi +index caf2707..b1e8145 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi +@@ -71,10 +71,6 @@ + status = "okay"; + }; + +-&usb { +- power-domains = <&power RPI_POWER_DOMAIN_USB>; +-}; +- + &v3d { + power-domains = <&power RPI_POWER_DOMAIN_V3D>; + }; +-- +2.7.3 + +From 6af83c5ff7f5514f32b1b3fa6d8d7dfe77e3acce Mon Sep 17 00:00:00 2001 +From: Stefan Wahren +Date: Sun, 17 Jan 2016 14:59:00 +0000 +Subject: [PATCH 33/36] mmc: sdhci-iproc: Clean up platform allocations if + shdci init fails + +This patch adopts the changes from 475c9e43bfa7 ("mmc: sdhci-bcm2835: +Clean up platform allocations if sdhci init fails") to sdhci-iproc. + +Signed-off-by: Stefan Wahren +Acked-by: Scott Branden +Signed-off-by: Ulf Hansson +--- + drivers/mmc/host/sdhci-iproc.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c +index 3b423b0..e22060a 100644 +--- a/drivers/mmc/host/sdhci-iproc.c ++++ b/drivers/mmc/host/sdhci-iproc.c +@@ -213,7 +213,11 @@ static int sdhci_iproc_probe(struct platform_device *pdev) + host->caps1 = iproc_host->data->caps1; + } + +- return sdhci_add_host(host); ++ ret = sdhci_add_host(host); ++ if (ret) ++ goto err; ++ ++ return 0; + + err: + sdhci_pltfm_free(pdev); +-- +2.7.3 + +From 1565145761d5d94991e4763001c9e60c655818f1 Mon Sep 17 00:00:00 2001 +From: Stefan Wahren +Date: Sun, 17 Jan 2016 14:59:01 +0000 +Subject: [PATCH 34/36] mmc: sdhci-iproc: Actually enable the clock + +The RPi firmware-based clocks driver can actually disable +unused clocks, so when switching to use it we ended up losing +our MMC clock once all devices were probed. + +This patch adopts the changes from 1e5a0a9a58e2 ("mmc: sdhci-bcm2835: +Actually enable the clock") to sdhci-iproc. + +Signed-off-by: Stefan Wahren +Acked-by: Scott Branden +Signed-off-by: Ulf Hansson +--- + drivers/mmc/host/sdhci-iproc.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c +index e22060a..55bc348 100644 +--- a/drivers/mmc/host/sdhci-iproc.c ++++ b/drivers/mmc/host/sdhci-iproc.c +@@ -207,6 +207,11 @@ static int sdhci_iproc_probe(struct platform_device *pdev) + ret = PTR_ERR(pltfm_host->clk); + goto err; + } ++ ret = clk_prepare_enable(pltfm_host->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable host clk\n"); ++ goto err; ++ } + + if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) { + host->caps = iproc_host->data->caps; +@@ -215,10 +220,12 @@ static int sdhci_iproc_probe(struct platform_device *pdev) + + ret = sdhci_add_host(host); + if (ret) +- goto err; ++ goto err_clk; + + return 0; + ++err_clk: ++ clk_disable_unprepare(pltfm_host->clk); + err: + sdhci_pltfm_free(pdev); + return ret; +-- +2.7.3 + +From 49ebf153a97a0840c1e54f934411aceb93bbdee4 Mon Sep 17 00:00:00 2001 +From: Stefan Wahren +Date: Wed, 27 Jan 2016 22:25:40 +0000 +Subject: [PATCH 35/36] mmc: sdhci-iproc: define MMC caps in platform data + +This patch moves the definition of the MMC capabilities +from the probe function into iproc platform data. After +that we are able to add support for another platform more +easily. + +Signed-off-by: Stefan Wahren +Suggested-by: Stephen Warren +Acked-by: Scott Branden +Acked-by: Stephen Warren +Signed-off-by: Ulf Hansson +--- + drivers/mmc/host/sdhci-iproc.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c +index 55bc348..cdc6c4a 100644 +--- a/drivers/mmc/host/sdhci-iproc.c ++++ b/drivers/mmc/host/sdhci-iproc.c +@@ -26,6 +26,7 @@ struct sdhci_iproc_data { + const struct sdhci_pltfm_data *pdata; + u32 caps; + u32 caps1; ++ u32 mmc_caps; + }; + + struct sdhci_iproc_host { +@@ -165,6 +166,7 @@ static const struct sdhci_iproc_data iproc_data = { + .pdata = &sdhci_iproc_pltfm_data, + .caps = 0x05E90000, + .caps1 = 0x00000064, ++ .mmc_caps = MMC_CAP_1_8V_DDR, + }; + + static const struct of_device_id sdhci_iproc_of_match[] = { +@@ -199,8 +201,7 @@ static int sdhci_iproc_probe(struct platform_device *pdev) + mmc_of_parse(host->mmc); + sdhci_get_of_property(pdev); + +- /* Enable EMMC 1/8V DDR capable */ +- host->mmc->caps |= MMC_CAP_1_8V_DDR; ++ host->mmc->caps |= iproc_host->data->mmc_caps; + + pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pltfm_host->clk)) { +-- +2.7.3 + +From 208897fb02fa78d06f960916bc3781c8a060ab72 Mon Sep 17 00:00:00 2001 +From: Stefan Wahren +Date: Wed, 27 Jan 2016 22:25:41 +0000 +Subject: [PATCH 36/36] mmc: sdhci-iproc: add bcm2835 support + +Scott Branden from Broadcom said that the BCM2835 eMMC IP core is +very similar to IPROC and share most of the quirks. So use this driver +instead of separate one. + +The sdhci-iproc contains a better workaround for the clock domain +crossing problem which doesn't need any delays. This results in a +better write performance. + +Btw we get the rid of the SDHCI_CAPABILITIES hack in the sdhci_readl +function. + +Suggested-by: Scott Branden +Signed-off-by: Stefan Wahren +Acked-by: Eric Anholt +Acked-by: Scott Branden +Acked-by: Stephen Warren +Signed-off-by: Ulf Hansson +--- + drivers/mmc/host/Kconfig | 6 +++--- + drivers/mmc/host/sdhci-iproc.c | 15 +++++++++++++++ + 2 files changed, 18 insertions(+), 3 deletions(-) + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 1526b8a..60de1e4 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -318,15 +318,15 @@ config MMC_SDHCI_F_SDH30 + If unsure, say N. + + config MMC_SDHCI_IPROC +- tristate "SDHCI platform support for the iProc SD/MMC Controller" +- depends on ARCH_BCM_IPROC || COMPILE_TEST ++ tristate "SDHCI support for the BCM2835 & iProc SD/MMC Controller" ++ depends on ARCH_BCM2835 || ARCH_BCM_IPROC || COMPILE_TEST + depends on MMC_SDHCI_PLTFM + default ARCH_BCM_IPROC + select MMC_SDHCI_IO_ACCESSORS + help + This selects the iProc SD/MMC controller. + +- If you have an IPROC platform with SD or MMC devices, ++ If you have a BCM2835 or IPROC platform with SD or MMC devices, + say Y or M here. + + If unsure, say N. +diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c +index cdc6c4a..871c92c 100644 +--- a/drivers/mmc/host/sdhci-iproc.c ++++ b/drivers/mmc/host/sdhci-iproc.c +@@ -169,7 +169,22 @@ static const struct sdhci_iproc_data iproc_data = { + .mmc_caps = MMC_CAP_1_8V_DDR, + }; + ++static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = { ++ .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | ++ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | ++ SDHCI_QUIRK_MISSING_CAPS, ++ .ops = &sdhci_iproc_ops, ++}; ++ ++static const struct sdhci_iproc_data bcm2835_data = { ++ .pdata = &sdhci_bcm2835_pltfm_data, ++ .caps = SDHCI_CAN_VDD_330, ++ .caps1 = 0x00000000, ++ .mmc_caps = 0x00000000, ++}; ++ + static const struct of_device_id sdhci_iproc_of_match[] = { ++ { .compatible = "brcm,bcm2835-sdhci", .data = &bcm2835_data }, + { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_data }, + { } + }; +-- +2.7.3 + diff --git a/bcm283x-vc4-fixes.patch b/bcm283x-vc4-fixes.patch index 89793b62b..3f77b7485 100644 --- a/bcm283x-vc4-fixes.patch +++ b/bcm283x-vc4-fixes.patch @@ -133,237 +133,6 @@ index 160942a..9ecd6ff 100644 -- 2.9.3 -From 107d3188b3723840deddaa5efeffcaf167e462f2 Mon Sep 17 00:00:00 2001 -From: Eric Anholt -Date: Wed, 28 Sep 2016 08:42:42 -0700 -Subject: [PATCH 3/4] drm/vc4: Fix races when the CS reads from render targets. - -With the introduction of bin/render pipelining, the previous job may -not be completed when we start binning the next one. If the previous -job wrote our VBO, IB, or CS textures, then the binning stage might -get stale or uninitialized results. - -Fixes the major rendering failure in glmark2 -b terrain. - -Signed-off-by: Eric Anholt -Fixes: ca26d28bbaa3 ("drm/vc4: improve throughput by pipelining binning and rendering jobs") -Cc: stable@vger.kernel.org ---- - drivers/gpu/drm/vc4/vc4_drv.h | 19 ++++++++++++++++++- - drivers/gpu/drm/vc4/vc4_gem.c | 13 +++++++++++++ - drivers/gpu/drm/vc4/vc4_render_cl.c | 21 +++++++++++++++++---- - drivers/gpu/drm/vc4/vc4_validate.c | 17 ++++++++++++++--- - 4 files changed, 62 insertions(+), 8 deletions(-) - -diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h -index 428e249..f696b75 100644 ---- a/drivers/gpu/drm/vc4/vc4_drv.h -+++ b/drivers/gpu/drm/vc4/vc4_drv.h -@@ -122,9 +122,16 @@ to_vc4_dev(struct drm_device *dev) - struct vc4_bo { - struct drm_gem_cma_object base; - -- /* seqno of the last job to render to this BO. */ -+ /* seqno of the last job to render using this BO. */ - uint64_t seqno; - -+ /* seqno of the last job to use the RCL to write to this BO. -+ * -+ * Note that this doesn't include binner overflow memory -+ * writes. -+ */ -+ uint64_t write_seqno; -+ - /* List entry for the BO's position in either - * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list - */ -@@ -216,6 +223,9 @@ struct vc4_exec_info { - /* Sequence number for this bin/render job. */ - uint64_t seqno; - -+ /* Latest write_seqno of any BO that binning depends on. */ -+ uint64_t bin_dep_seqno; -+ - /* Last current addresses the hardware was processing when the - * hangcheck timer checked on us. - */ -@@ -230,6 +240,13 @@ struct vc4_exec_info { - struct drm_gem_cma_object **bo; - uint32_t bo_count; - -+ /* List of BOs that are being written by the RCL. Other than -+ * the binner temporary storage, this is all the BOs written -+ * by the job. -+ */ -+ struct drm_gem_cma_object *rcl_write_bo[4]; -+ uint32_t rcl_write_bo_count; -+ - /* Pointers for our position in vc4->job_list */ - struct list_head head; - -diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c -index b262c5c..ae1609e 100644 ---- a/drivers/gpu/drm/vc4/vc4_gem.c -+++ b/drivers/gpu/drm/vc4/vc4_gem.c -@@ -471,6 +471,11 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno) - list_for_each_entry(bo, &exec->unref_list, unref_head) { - bo->seqno = seqno; - } -+ -+ for (i = 0; i < exec->rcl_write_bo_count; i++) { -+ bo = to_vc4_bo(&exec->rcl_write_bo[i]->base); -+ bo->write_seqno = seqno; -+ } - } - - /* Queues a struct vc4_exec_info for execution. If no job is -@@ -673,6 +678,14 @@ vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) - goto fail; - - ret = vc4_validate_shader_recs(dev, exec); -+ if (ret) -+ goto fail; -+ -+ /* Block waiting on any previous rendering into the CS's VBO, -+ * IB, or textures, so that pixels are actually written by the -+ * time we try to read them. -+ */ -+ ret = vc4_wait_for_seqno(dev, exec->bin_dep_seqno, ~0ull, true); - - fail: - drm_free_large(temp); -diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c -index 0f12418..08886a3 100644 ---- a/drivers/gpu/drm/vc4/vc4_render_cl.c -+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c -@@ -45,6 +45,8 @@ struct vc4_rcl_setup { - - struct drm_gem_cma_object *rcl; - u32 next_offset; -+ -+ u32 next_write_bo_index; - }; - - static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val) -@@ -407,6 +409,8 @@ static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec, - if (!*obj) - return -EINVAL; - -+ exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj; -+ - if (surf->offset & 0xf) { - DRM_ERROR("MSAA write must be 16b aligned.\n"); - return -EINVAL; -@@ -417,7 +421,8 @@ static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec, - - static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, - struct drm_gem_cma_object **obj, -- struct drm_vc4_submit_rcl_surface *surf) -+ struct drm_vc4_submit_rcl_surface *surf, -+ bool is_write) - { - uint8_t tiling = VC4_GET_FIELD(surf->bits, - VC4_LOADSTORE_TILE_BUFFER_TILING); -@@ -440,6 +445,9 @@ static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, - if (!*obj) - return -EINVAL; - -+ if (is_write) -+ exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj; -+ - if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { - if (surf == &exec->args->zs_write) { - DRM_ERROR("general zs write may not be a full-res.\n"); -@@ -542,6 +550,8 @@ vc4_rcl_render_config_surface_setup(struct vc4_exec_info *exec, - if (!*obj) - return -EINVAL; - -+ exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj; -+ - if (tiling > VC4_TILING_FORMAT_LT) { - DRM_ERROR("Bad tiling format\n"); - return -EINVAL; -@@ -599,15 +609,18 @@ int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec) - if (ret) - return ret; - -- ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); -+ ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read, -+ false); - if (ret) - return ret; - -- ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read); -+ ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read, -+ false); - if (ret) - return ret; - -- ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write); -+ ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write, -+ true); - if (ret) - return ret; - -diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c -index 9ce1d0a..26503e3 100644 ---- a/drivers/gpu/drm/vc4/vc4_validate.c -+++ b/drivers/gpu/drm/vc4/vc4_validate.c -@@ -267,6 +267,9 @@ validate_indexed_prim_list(VALIDATE_ARGS) - if (!ib) - return -EINVAL; - -+ exec->bin_dep_seqno = max(exec->bin_dep_seqno, -+ to_vc4_bo(&ib->base)->write_seqno); -+ - if (offset > ib->base.size || - (ib->base.size - offset) / index_size < length) { - DRM_ERROR("IB access overflow (%d + %d*%d > %zd)\n", -@@ -555,8 +558,7 @@ static bool - reloc_tex(struct vc4_exec_info *exec, - void *uniform_data_u, - struct vc4_texture_sample_info *sample, -- uint32_t texture_handle_index) -- -+ uint32_t texture_handle_index, bool is_cs) - { - struct drm_gem_cma_object *tex; - uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]); -@@ -714,6 +716,11 @@ reloc_tex(struct vc4_exec_info *exec, - - *validated_p0 = tex->paddr + p0; - -+ if (is_cs) { -+ exec->bin_dep_seqno = max(exec->bin_dep_seqno, -+ to_vc4_bo(&tex->base)->write_seqno); -+ } -+ - return true; - fail: - DRM_INFO("Texture p0 at %d: 0x%08x\n", sample->p_offset[0], p0); -@@ -835,7 +842,8 @@ validate_gl_shader_rec(struct drm_device *dev, - if (!reloc_tex(exec, - uniform_data_u, - &validated_shader->texture_samples[tex], -- texture_handles_u[tex])) { -+ texture_handles_u[tex], -+ i == 2)) { - return -EINVAL; - } - } -@@ -867,6 +875,9 @@ validate_gl_shader_rec(struct drm_device *dev, - uint32_t stride = *(uint8_t *)(pkt_u + o + 5); - uint32_t max_index; - -+ exec->bin_dep_seqno = max(exec->bin_dep_seqno, -+ to_vc4_bo(&vbo->base)->write_seqno); -+ - if (state->addr & 0x8) - stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff; - --- -2.9.3 - From f379f5432e4b74e3d1d894ce2fefbe1b8a3c24fd Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 28 Sep 2016 19:20:44 -0700 @@ -634,3 +403,280 @@ index 9ecd6ff..a4b5370 100644 -- 2.9.3 +From c4e634ce412d97f0e61223b2a5b3f8f9600cd4dc Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 30 Sep 2016 10:07:27 -0700 +Subject: clk: bcm2835: Clamp the PLL's requested rate to the hardware limits. + +Fixes setting low-resolution video modes on HDMI. Now the PLLH_PIX +divider adjusts itself until the PLLH is within bounds. + +Signed-off-by: Eric Anholt +Signed-off-by: Stephen Boyd +--- + drivers/clk/bcm/clk-bcm2835.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c +index b68bf57..8c7763f 100644 +--- a/drivers/clk/bcm/clk-bcm2835.c ++++ b/drivers/clk/bcm/clk-bcm2835.c +@@ -502,8 +502,12 @@ static long bcm2835_pll_rate_from_divisors(unsigned long parent_rate, + static long bcm2835_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) + { ++ struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); ++ const struct bcm2835_pll_data *data = pll->data; + u32 ndiv, fdiv; + ++ rate = clamp(rate, data->min_rate, data->max_rate); ++ + bcm2835_pll_choose_ndiv_and_fdiv(rate, *parent_rate, &ndiv, &fdiv); + + return bcm2835_pll_rate_from_divisors(*parent_rate, ndiv, fdiv, 1); +@@ -608,13 +612,6 @@ static int bcm2835_pll_set_rate(struct clk_hw *hw, + u32 ana[4]; + int i; + +- if (rate < data->min_rate || rate > data->max_rate) { +- dev_err(cprman->dev, "%s: rate out of spec: %lu vs (%lu, %lu)\n", +- clk_hw_get_name(hw), rate, +- data->min_rate, data->max_rate); +- return -EINVAL; +- } +- + if (rate > data->max_fb_rate) { + use_fb_prediv = true; + rate /= 2; +-- +cgit v0.12 + +From e69fdcca836f0b81a2260b69429c8622a80ea891 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 1 Jun 2016 12:05:33 -0700 +Subject: clk: bcm2835: Mark the VPU clock as critical + +The VPU clock is also the clock for our AXI bus, so we really can't +disable it. This might have happened during boot if, for example, +uart1 (aux_uart clock) probed and was then disabled before the other +consumers of the VPU clock had probed. + +Signed-off-by: Eric Anholt +Acked-by: Martin Sperl +Signed-off-by: Stephen Boyd +--- + drivers/clk/bcm/clk-bcm2835.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c +index 7a79708..d9db03c 100644 +--- a/drivers/clk/bcm/clk-bcm2835.c ++++ b/drivers/clk/bcm/clk-bcm2835.c +@@ -443,6 +443,8 @@ struct bcm2835_clock_data { + /* Number of fractional bits in the divider */ + u32 frac_bits; + ++ u32 flags; ++ + bool is_vpu_clock; + bool is_mash_clock; + }; +@@ -1230,7 +1232,7 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman, + init.parent_names = parents; + init.num_parents = data->num_mux_parents; + init.name = data->name; +- init.flags = CLK_IGNORE_UNUSED; ++ init.flags = data->flags | CLK_IGNORE_UNUSED; + + if (data->is_vpu_clock) { + init.ops = &bcm2835_vpu_clock_clk_ops; +@@ -1649,6 +1651,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { + .div_reg = CM_VPUDIV, + .int_bits = 12, + .frac_bits = 8, ++ .flags = CLK_IS_CRITICAL, + .is_vpu_clock = true), + + /* clocks with per parent mux */ +-- +cgit v0.12 + +From eddcbe8398fc7103fccd22aa6df6917caf0123bf Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 1 Jun 2016 12:05:34 -0700 +Subject: clk: bcm2835: Mark GPIO clocks enabled at boot as critical + +These divide off of PLLD_PER and are used for the ethernet and wifi +PHYs source PLLs. Neither of them is currently represented by a phy +device that would grab the clock for us. + +This keeps other drivers from killing the networking PHYs when they +disable their own clocks and trigger PLLD_PER's refcount going to 0. + +Signed-off-by: Eric Anholt +Acked-by: Martin Sperl +Signed-off-by: Stephen Boyd +--- + drivers/clk/bcm/clk-bcm2835.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c +index d9db03c..400615b 100644 +--- a/drivers/clk/bcm/clk-bcm2835.c ++++ b/drivers/clk/bcm/clk-bcm2835.c +@@ -1239,6 +1239,12 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman, + } else { + init.ops = &bcm2835_clock_clk_ops; + init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; ++ ++ /* If the clock wasn't actually enabled at boot, it's not ++ * critical. ++ */ ++ if (!(cprman_read(cprman, data->ctl_reg) & CM_ENABLE)) ++ init.flags &= ~CLK_IS_CRITICAL; + } + + clock = devm_kzalloc(cprman->dev, sizeof(*clock), GFP_KERNEL); +@@ -1708,13 +1714,15 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { + .div_reg = CM_GP1DIV, + .int_bits = 12, + .frac_bits = 12, ++ .flags = CLK_IS_CRITICAL, + .is_mash_clock = true), + [BCM2835_CLOCK_GP2] = REGISTER_PER_CLK( + .name = "gp2", + .ctl_reg = CM_GP2CTL, + .div_reg = CM_GP2DIV, + .int_bits = 12, +- .frac_bits = 12), ++ .frac_bits = 12, ++ .flags = CLK_IS_CRITICAL), + + /* HDMI state machine */ + [BCM2835_CLOCK_HSM] = REGISTER_PER_CLK( +-- +cgit v0.12 + +From 9e400c5cc5c105e35216ac59a346f20cdd7613be Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 1 Jun 2016 12:05:35 -0700 +Subject: clk: bcm2835: Mark the CM SDRAM clock's parent as critical + +While the SDRAM is being driven by its dedicated PLL most of the time, +there is a little loop running in the firmware that periodically turns +on the CM SDRAM clock (using its pre-initialized parent) and switches +SDRAM to using the CM clock to do PVT recalibration. + +This avoids system hangs if we choose SDRAM's parent for some other +clock, then disable that clock. + +Signed-off-by: Eric Anholt +Acked-by: Martin Sperl +Signed-off-by: Stephen Boyd +--- + drivers/clk/bcm/clk-bcm2835.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c +index 400615b..c6420b3 100644 +--- a/drivers/clk/bcm/clk-bcm2835.c ++++ b/drivers/clk/bcm/clk-bcm2835.c +@@ -36,6 +36,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -1801,6 +1802,25 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { + .ctl_reg = CM_PERIICTL), + }; + ++/* ++ * Permanently take a reference on the parent of the SDRAM clock. ++ * ++ * While the SDRAM is being driven by its dedicated PLL most of the ++ * time, there is a little loop running in the firmware that ++ * periodically switches the SDRAM to using our CM clock to do PVT ++ * recalibration, with the assumption that the previously configured ++ * SDRAM parent is still enabled and running. ++ */ ++static int bcm2835_mark_sdc_parent_critical(struct clk *sdc) ++{ ++ struct clk *parent = clk_get_parent(sdc); ++ ++ if (IS_ERR(parent)) ++ return PTR_ERR(parent); ++ ++ return clk_prepare_enable(parent); ++} ++ + static int bcm2835_clk_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -1810,6 +1830,7 @@ static int bcm2835_clk_probe(struct platform_device *pdev) + const struct bcm2835_clk_desc *desc; + const size_t asize = ARRAY_SIZE(clk_desc_array); + size_t i; ++ int ret; + + cprman = devm_kzalloc(dev, + sizeof(*cprman) + asize * sizeof(*clks), +@@ -1840,6 +1861,10 @@ static int bcm2835_clk_probe(struct platform_device *pdev) + clks[i] = desc->clk_register(cprman, desc->data); + } + ++ ret = bcm2835_mark_sdc_parent_critical(clks[BCM2835_CLOCK_SDRAM]); ++ if (ret) ++ return ret; ++ + return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + &cprman->onecell); + } +-- +cgit v0.12 + +From 30772942cc1095c3129eecfa182e2c568e566b9d Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Thu, 13 Oct 2016 11:54:31 +0300 +Subject: [PATCH] drm/vc4: Fix a couple error codes in vc4_cl_lookup_bos() + +If the allocation fails the current code returns success. If +copy_from_user() fails it returns the number of bytes remaining instead +of -EFAULT. + +Fixes: d5b1a78a772f ("drm/vc4: Add support for drawing 3D frames.") +Signed-off-by: Dan Carpenter +Reviewed-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_gem.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c +index ae1609e..4050540 100644 +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -548,14 +548,15 @@ vc4_cl_lookup_bos(struct drm_device *dev, + + handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t)); + if (!handles) { ++ ret = -ENOMEM; + DRM_ERROR("Failed to allocate incoming GEM handles\n"); + goto fail; + } + +- ret = copy_from_user(handles, +- (void __user *)(uintptr_t)args->bo_handles, +- exec->bo_count * sizeof(uint32_t)); +- if (ret) { ++ if (copy_from_user(handles, ++ (void __user *)(uintptr_t)args->bo_handles, ++ exec->bo_count * sizeof(uint32_t))) { ++ ret = -EFAULT; + DRM_ERROR("Failed to copy in GEM handles\n"); + goto fail; + } +-- +2.9.3 + diff --git a/block-blkg_destroy_all-should-clear-q-root_blkg-and-.patch b/block-blkg_destroy_all-should-clear-q-root_blkg-and-.patch new file mode 100644 index 000000000..be5eddebc --- /dev/null +++ b/block-blkg_destroy_all-should-clear-q-root_blkg-and-.patch @@ -0,0 +1,64 @@ +From a08748fb2221ef03d54071e5ddfcc1b0cee6961c Mon Sep 17 00:00:00 2001 +From: Tejun Heo +Date: Sat, 5 Sep 2015 15:47:36 -0400 +Subject: [PATCH] block: blkg_destroy_all() should clear q->root_blkg and + ->root_rl.blkg + +While making the root blkg unconditional, ec13b1d6f0a0 ("blkcg: always +create the blkcg_gq for the root blkcg") removed the part which clears +q->root_blkg and ->root_rl.blkg during q exit. This leaves the two +pointers dangling after blkg_destroy_all(). blk-throttle exit path +performs blkg traversals and dereferences ->root_blkg and can lead to +the following oops. + + BUG: unable to handle kernel NULL pointer dereference at 0000000000000558 + IP: [] __blkg_lookup+0x26/0x70 + ... + task: ffff88001b4e2580 ti: ffff88001ac0c000 task.ti: ffff88001ac0c000 + RIP: 0010:[] [] __blkg_lookup+0x26/0x70 + ... + Call Trace: + [] blk_throtl_drain+0x5a/0x110 + [] blkcg_drain_queue+0x18/0x20 + [] __blk_drain_queue+0xc0/0x170 + [] blk_queue_bypass_start+0x61/0x80 + [] blkcg_deactivate_policy+0x39/0x100 + [] blk_throtl_exit+0x38/0x50 + [] blkcg_exit_queue+0x3e/0x50 + [] blk_release_queue+0x1e/0xc0 + ... + +While the bug is a straigh-forward use-after-free bug, it is tricky to +reproduce because blkg release is RCU protected and the rest of exit +path usually finishes before RCU grace period. + +This patch fixes the bug by updating blkg_destro_all() to clear +q->root_blkg and ->root_rl.blkg. + +Signed-off-by: Tejun Heo +Reported-by: "Richard W.M. Jones" +Reported-by: Josh Boyer +Link: http://lkml.kernel.org/g/CA+5PVA5rzQ0s4723n5rHBcxQa9t0cW8BPPBekr_9aMRoWt2aYg@mail.gmail.com +Fixes: ec13b1d6f0a0 ("blkcg: always create the blkcg_gq for the root blkcg") +Cc: stable@vger.kernel.org # v4.2+ +--- + block/blk-cgroup.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c +index d6283b3f5db5..9cc48d1d7abb 100644 +--- a/block/blk-cgroup.c ++++ b/block/blk-cgroup.c +@@ -387,6 +387,9 @@ static void blkg_destroy_all(struct request_queue *q) + blkg_destroy(blkg); + spin_unlock(&blkcg->lock); + } ++ ++ q->root_blkg = NULL; ++ q->root_rl.blkg = NULL; + } + + /* +-- +2.4.3 + diff --git a/config-arm-generic b/config-arm-generic index 7a88c2d02..ba316f27f 100644 --- a/config-arm-generic +++ b/config-arm-generic @@ -95,12 +95,12 @@ CONFIG_EDAC_LEGACY_SYSFS=y # Regulators CONFIG_REGULATOR=y -CONFIG_RFKILL_REGULATOR=m +CONFIG_REGULATOR_GPIO=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_VIRTUAL_CONSUMER=m CONFIG_REGULATOR_USERSPACE_CONSUMER=m -CONFIG_REGULATOR_GPIO=m CONFIG_REGULATOR_PWM=m +CONFIG_RFKILL_REGULATOR=m # ARM VExpress CONFIG_ARCH_VEXPRESS=y @@ -170,6 +170,7 @@ CONFIG_SERIAL_MVEBU_CONSOLE=y CONFIG_MVEBU_DEVBUS=y CONFIG_MVEBU_MBUS=y CONFIG_PCI_MVEBU=y +CONFIG_PCI_AARDVARK=y CONFIG_PCIE_ARMADA_8K=y CONFIG_MV_XOR=y CONFIG_CRYPTO_DEV_MV_CESA=m @@ -195,9 +196,9 @@ CONFIG_USB_MV_UDC=m CONFIG_USB_XHCI_MVEBU=m CONFIG_PHY_MVEBU_SATA=y CONFIG_AHCI_MVEBU=m +CONFIG_I2C_PXA=m # CONFIG_CACHE_FEROCEON_L2 is not set # CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH is not set -# CONFIG_PCI_AARDVARK is not set # Rockchips CONFIG_ARCH_ROCKCHIP=y diff --git a/config-arm64 b/config-arm64 index ae98cc5ca..0bc9b30bd 100644 --- a/config-arm64 +++ b/config-arm64 @@ -101,7 +101,7 @@ CONFIG_ACPI_CUSTOM_METHOD=m CONFIG_ACPI_NFIT=m # CONFIG_ACPI_NFIT_DEBUG is not set CONFIG_PCC=y -CONFIG_ACPI_CPPC_CPUFREQ=y +# CONFIG_ACPI_CPPC_CPUFREQ is not set CONFIG_ACPI_PROCESSOR=y CONFIG_ACPI_THERMAL=y CONFIG_I2C_SCMI=m diff --git a/config-armv7 b/config-armv7 index 0207f9ab9..55f188c22 100644 --- a/config-armv7 +++ b/config-armv7 @@ -518,7 +518,6 @@ CONFIG_MACH_MMP2_DT=y CONFIG_SERIAL_PXA=y CONFIG_SERIAL_PXA_CONSOLE=y CONFIG_KEYBOARD_PXA27x=y -CONFIG_I2C_PXA=m # CONFIG_I2C_PXA_SLAVE is not set CONFIG_SND_MMP_SOC=y CONFIG_SND_PXA910_SOC=m diff --git a/config-armv7-generic b/config-armv7-generic index f1e544ed9..47918a98b 100644 --- a/config-armv7-generic +++ b/config-armv7-generic @@ -209,11 +209,11 @@ CONFIG_I2C_SUN6I_P2WI=m CONFIG_GPIO_PCF857X=m CONFIG_TOUCHSCREEN_SUN4I=m CONFIG_MFD_AXP20X=y -CONFIG_MFD_AXP20X_I2C=m -CONFIG_MFD_AXP20X_RSB=m +CONFIG_MFD_AXP20X_I2C=y +CONFIG_MFD_AXP20X_RSB=y CONFIG_AXP20X_POWER=m CONFIG_INPUT_AXP20X_PEK=m -CONFIG_REGULATOR_AXP20X=m +CONFIG_REGULATOR_AXP20X=y CONFIG_AXP288_FUEL_GAUGE=m CONFIG_AXP288_ADC=m CONFIG_EXTCON_AXP288=m @@ -228,14 +228,14 @@ CONFIG_RTC_DRV_SUN6I=m CONFIG_MTD_NAND_SUNXI=m CONFIG_SERIO_SUN4I_PS2=m CONFIG_KEYBOARD_SUN4I_LRADC=m -CONFIG_PWM_SUN4I=m +CONFIG_PWM_SUN4I=y CONFIG_CAN_SUN4I=m CONFIG_USB_MUSB_SUNXI=m CONFIG_CRYPTO_DEV_SUN4I_SS=m CONFIG_SND_SUN4I_CODEC=m CONFIG_SND_SUN4I_SPDIF=m CONFIG_SND_SUN4I_I2S=m -CONFIG_SUNXI_RSB=m +CONFIG_SUNXI_RSB=y CONFIG_NVMEM_SUNXI_SID=m # Exynos @@ -330,6 +330,9 @@ CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF=m CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994=m CONFIG_SND_SOC_SMDK_WM8994_PCM=m CONFIG_SND_SOC_SNOW=m +CONFIG_SND_SOC_MAX98090=m +CONFIG_SND_SOC_MAX98095=m +CONFIG_SND_SAMSUNG_I2S=m CONFIG_SND_SOC_ODROIDX2=m CONFIG_EXYNOS_AUDSS_CLK_CON=m # CONFIG_EXYNOS_IOMMU_DEBUG is not set @@ -351,6 +354,8 @@ CONFIG_CHARGER_MAX8997=m CONFIG_LEDS_MAX8997=m CONFIG_RTC_DRV_MAX8997=m CONFIG_EXTCON_MAX8997=m +CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631=m +CONFIG_SND_SOC_RT5631=m # Tegra CONFIG_ARCH_TEGRA_114_SOC=y @@ -401,11 +406,11 @@ CONFIG_USB_EHCI_HCD_ORION=m CONFIG_MVPP2=m CONFIG_COMMON_CLK_SI5351=m CONFIG_RTC_DRV_ARMADA38X=m +CONFIG_I2C_PXA=m # CONFIG_CACHE_FEROCEON_L2 is not set # CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH is not set CONFIG_LEDS_NS2=m -CONFIG_SERIAL_MVEBU_UART=y -# CONFIG_SERIAL_MVEBU_CONSOLE is not set +CONFIG_LEDS_PCA963X=m # CONFIG_PCIE_ARMADA_8K is not set # DRM panels diff --git a/config-local b/config-local index 8c32be5be..dea33a65c 100644 --- a/config-local +++ b/config-local @@ -1,2 +1,70 @@ # This file is intentionally left empty in the stock kernel. Its a nicety # added for those wanting to do custom rebuilds with altered config opts. + +CONFIG_IOSCHED_BFQ=m + +CONFIG_SCHED_BFS=y +# BFS successor see https://pf.natalenko.name/news/?p=207 +CONFIG_SCHED_MUQSS=y + +CONFIG_SMT_NICE=n +CONFIG_CGROUP_BFQIO=y +CONFIG_BFQ_GROUP_IOSCHED=y +CONFIG_ZCACHE=y +CONFIG_ZCACHE_WRITEBACK=y +# CONFIG_ZCACHE_DEBUG is not set +CONFIG_KSM=y +CONFIG_UKSM=y + +# TuxOnIce: +# https://groups.google.com/forum/#!msg/chakra-devel/0azu1a22LTs/bEwKElM2CYMJ +CONFIG_TOI_CORE=y +CONFIG_TOI_FILE=y +CONFIG_TOI_SWAP=y +CONFIG_TOI_CRYPTO=y +CONFIG_TOI_USERUI=y +CONFIG_TOI_USERUI_DEFAULT_PATH="/usr/sbin/tuxoniceui" +CONFIG_TOI_DEFAULT_IMAGE_SIZE_LIMIT=-2 +# CONFIG_TOI_KEEP_IMAGE is not set +CONFIG_TOI_REPLACE_SWSUSP=y +# CONFIG_TOI_IGNORE_LATE_INITCALL is not set +CONFIG_TOI_DEFAULT_WAIT=25 +CONFIG_TOI_DEFAULT_EXTRA_PAGES_ALLOWANCE=7500 +# CONFIG_TOI_CHECKSUM is not set +CONFIG_TOI=y +CONFIG_TOI_EXPORTS=y +CONFIG_TOI_ZRAM_SUPPORT=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_TOI_INCREMENTAL=y + +# TuxOnIce - ftrace: Answer for me to try resolve TuxOnIce problem finding ftrace: https://pf.natalenko.name/forum/index.php?topic=257.0 +CONFIG_HAVE_KPROBES_ON_FTRACE=y +# CONFIG_PSTORE_FTRACE is not set +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_FTRACE=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_DYNAMIC_FTRACE=y +CONFIG_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_FTRACE_MCOUNT_RECORD=y +# CONFIG_FTRACE_STARTUP_TEST is not set + +# Ulatencyd: Settings from https://wiki.archlinux.org/index.php/Ulatencyd +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +CONFIG_CFQ_GROUP_IOSCHED=y + +CONFIG_ZRAM_LZ4_COMPRESS=y +CONFIG_ZRAM_DEBUG=n + +CONFIG_MNATIVE=y diff --git a/criu-no-expert.patch b/criu-no-expert.patch index f7e1ffff1..44b510210 100644 --- a/criu-no-expert.patch +++ b/criu-no-expert.patch @@ -12,15 +12,6 @@ diff --git a/init/Kconfig b/init/Kconfig index 648bb79d6b73..860ca236975f 100644 --- a/init/Kconfig +++ b/init/Kconfig -@@ -1137,7 +1137,7 @@ config CGROUP_WRITEBACK - endif # CGROUPS - - config CHECKPOINT_RESTORE -- bool "Checkpoint/restore support" if EXPERT -+ bool "Checkpoint/restore support" - select PROC_CHILDREN - default n - help @@ -1149,7 +1149,7 @@ config CHECKPOINT_RESTORE If unsure, say N here. diff --git a/dcache-Handle-escaped-paths-in-prepend_path.patch b/dcache-Handle-escaped-paths-in-prepend_path.patch new file mode 100644 index 000000000..d5040607d --- /dev/null +++ b/dcache-Handle-escaped-paths-in-prepend_path.patch @@ -0,0 +1,65 @@ +From c0ea161a6e7158281f64bc6d41126da43cb08f14 Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Sat, 15 Aug 2015 13:36:12 -0500 +Subject: [PATCH 1/2] dcache: Handle escaped paths in prepend_path + +commit cde93be45a8a90d8c264c776fab63487b5038a65 upstream. + +A rename can result in a dentry that by walking up d_parent +will never reach it's mnt_root. For lack of a better term +I call this an escaped path. + +prepend_path is called by four different functions __d_path, +d_absolute_path, d_path, and getcwd. + +__d_path only wants to see paths are connected to the root it passes +in. So __d_path needs prepend_path to return an error. + +d_absolute_path similarly wants to see paths that are connected to +some root. Escaped paths are not connected to any mnt_root so +d_absolute_path needs prepend_path to return an error greater +than 1. So escaped paths will be treated like paths on lazily +unmounted mounts. + +getcwd needs to prepend "(unreachable)" so getcwd also needs +prepend_path to return an error. + +d_path is the interesting hold out. d_path just wants to print +something, and does not care about the weird cases. Which raises +the question what should be printed? + +Given that / should result in -ENOENT I +believe it is desirable for escaped paths to be printed as empty +paths. As there are not really any meaninful path components when +considered from the perspective of a mount tree. + +So tweak prepend_path to return an empty path with an new error +code of 3 when it encounters an escaped path. + +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Al Viro +--- + fs/dcache.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/fs/dcache.c b/fs/dcache.c +index 9b5fe503f6cb..e3b44ca75a1b 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -2926,6 +2926,13 @@ restart: + + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + struct mount *parent = ACCESS_ONCE(mnt->mnt_parent); ++ /* Escaped? */ ++ if (dentry != vfsmnt->mnt_root) { ++ bptr = *buffer; ++ blen = *buflen; ++ error = 3; ++ break; ++ } + /* Global root? */ + if (mnt != parent) { + dentry = ACCESS_ONCE(mnt->mnt_mountpoint); +-- +2.4.3 + diff --git a/drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch b/drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch new file mode 100644 index 000000000..70204e5e9 --- /dev/null +++ b/drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch @@ -0,0 +1,60 @@ +From a19afebb883f2a02ecf4b8d5a114ce6957a59238 Mon Sep 17 00:00:00 2001 +From: Thomas Hellstrom +Date: Wed, 26 Aug 2015 05:49:21 -0700 +Subject: [PATCH 2/2] drm/vmwgfx: Allow dropped masters render-node like access + on legacy nodes v2 + +Applications like gnome-shell may try to render after dropping master +privileges. Since the driver should now be safe against this scenario, +allow those applications to use their legacy node like a render node. + +v2: Add missing return statement. + +Signed-off-by: Thomas Hellstrom +Reviewed-by: Sinclair Yeh +--- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 7 ++++++- + drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 6 ++++++ + 2 files changed, 12 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +index a4766acd0ea2..d022b509f1ac 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +@@ -993,10 +993,15 @@ static struct vmw_master *vmw_master_check(struct drm_device *dev, + } + + /* +- * Check if we were previously master, but now dropped. ++ * Check if we were previously master, but now dropped. In that ++ * case, allow at least render node functionality. + */ + if (vmw_fp->locked_master) { + mutex_unlock(&dev->master_mutex); ++ ++ if (flags & DRM_RENDER_ALLOW) ++ return NULL; ++ + DRM_ERROR("Dropped master trying to access ioctl that " + "requires authentication.\n"); + return ERR_PTR(-EACCES); +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +index 4d0c98edeb6a..7fc3e8abd0c4 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +@@ -906,6 +906,12 @@ vmw_surface_handle_reference(struct vmw_private *dev_priv, + "surface reference.\n"); + return -EACCES; + } ++ if (ACCESS_ONCE(vmw_fpriv(file_priv)->locked_master)) { ++ DRM_ERROR("Locked master refused legacy " ++ "surface reference.\n"); ++ return -EACCES; ++ } ++ + handle = u_handle; + } + +-- +2.4.3 + diff --git a/filter-aarch64.sh b/filter-aarch64.sh old mode 100644 new mode 100755 diff --git a/filter-armv7hl.sh b/filter-armv7hl.sh old mode 100644 new mode 100755 diff --git a/filter-i686.sh b/filter-i686.sh old mode 100644 new mode 100755 diff --git a/filter-ppc64.sh b/filter-ppc64.sh old mode 100644 new mode 100755 diff --git a/filter-ppc64le.sh b/filter-ppc64le.sh old mode 100644 new mode 100755 diff --git a/filter-ppc64p7.sh b/filter-ppc64p7.sh old mode 100644 new mode 100755 diff --git a/filter-s390x.sh b/filter-s390x.sh old mode 100644 new mode 100755 diff --git a/filter-x86_64.sh b/filter-x86_64.sh old mode 100644 new mode 100755 diff --git a/iSCSI-let-session-recovery_tmo-sysfs-writes-persist.patch b/iSCSI-let-session-recovery_tmo-sysfs-writes-persist.patch new file mode 100644 index 000000000..174cccb02 --- /dev/null +++ b/iSCSI-let-session-recovery_tmo-sysfs-writes-persist.patch @@ -0,0 +1,89 @@ +From 9c8108a4d3a837c51a29f28229a06d97654eaeb6 Mon Sep 17 00:00:00 2001 +From: Chris Leech +Date: Tue, 16 Jun 2015 16:07:13 -0700 +Subject: iSCSI: let session recovery_tmo sysfs writes persist across recovery + +The iSCSI session recovery_tmo setting is writeable in sysfs, but it's +also set every time a connection is established when parameters are set +from iscsid over netlink. That results in the timeout being reset to +the default value after every recovery. + +The DM multipath tools want to use the sysfs interface to lower the +default timeout when there are multiple paths to fail over. It has +caused confusion that we have a writeable sysfs value that seem to keep +resetting itself. + +This patch adds an in-kernel flag that gets set once a sysfs write +occurs, and then ignores netlink parameter setting once it's been +modified via the sysfs interface. My thinking here is that the sysfs +interface is much simpler for external tools to influence the session +timeout, but if we're going to allow it to be modified directly we +should ensure that setting is maintained. + +Signed-off-by: Chris Leech +Reviewed-by: Mike Christie +Signed-off-by: James Bottomley + +diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c +index 55647aa..4c25539 100644 +--- a/drivers/scsi/scsi_transport_iscsi.c ++++ b/drivers/scsi/scsi_transport_iscsi.c +@@ -2042,6 +2042,7 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport, + session->transport = transport; + session->creator = -1; + session->recovery_tmo = 120; ++ session->recovery_tmo_sysfs_override = false; + session->state = ISCSI_SESSION_FREE; + INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); + INIT_LIST_HEAD(&session->sess_list); +@@ -2786,7 +2787,8 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) + switch (ev->u.set_param.param) { + case ISCSI_PARAM_SESS_RECOVERY_TMO: + sscanf(data, "%d", &value); +- session->recovery_tmo = value; ++ if (!session->recovery_tmo_sysfs_override) ++ session->recovery_tmo = value; + break; + default: + err = transport->set_param(conn, ev->u.set_param.param, +@@ -4049,13 +4051,15 @@ store_priv_session_##field(struct device *dev, \ + if ((session->state == ISCSI_SESSION_FREE) || \ + (session->state == ISCSI_SESSION_FAILED)) \ + return -EBUSY; \ +- if (strncmp(buf, "off", 3) == 0) \ ++ if (strncmp(buf, "off", 3) == 0) { \ + session->field = -1; \ +- else { \ ++ session->field##_sysfs_override = true; \ ++ } else { \ + val = simple_strtoul(buf, &cp, 0); \ + if (*cp != '\0' && *cp != '\n') \ + return -EINVAL; \ + session->field = val; \ ++ session->field##_sysfs_override = true; \ + } \ + return count; \ + } +@@ -4066,6 +4070,7 @@ store_priv_session_##field(struct device *dev, \ + static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR, \ + show_priv_session_##field, \ + store_priv_session_##field) ++ + iscsi_priv_session_rw_attr(recovery_tmo, "%d"); + + static struct attribute *iscsi_session_attrs[] = { +diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h +index 2555ee5..6183d20 100644 +--- a/include/scsi/scsi_transport_iscsi.h ++++ b/include/scsi/scsi_transport_iscsi.h +@@ -241,6 +241,7 @@ struct iscsi_cls_session { + + /* recovery fields */ + int recovery_tmo; ++ bool recovery_tmo_sysfs_override; + struct delayed_work recovery_work; + + unsigned int target_id; +-- +cgit v0.10.2 + diff --git a/kernel-panic-TPROXY-vanilla-4.7.1.patch b/kernel-panic-TPROXY-vanilla-4.7.1.patch new file mode 100644 index 000000000..9d045cabe --- /dev/null +++ b/kernel-panic-TPROXY-vanilla-4.7.1.patch @@ -0,0 +1,85 @@ +From patchwork Wed Aug 17 16:04:31 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: kernel panic TPROXY , vanilla 4.7.1 +From: Eric Dumazet +X-Patchwork-Id: 660174 +X-Patchwork-Delegate: davem@davemloft.net +Message-Id: <1471449871.29842.3.camel@edumazet-glaptop3.roam.corp.google.com> +To: Denys Fedoryshchenko +Cc: Linux Kernel Network Developers , + netfilter-devel@vger.kernel.org +Date: Wed, 17 Aug 2016 09:04:31 -0700 + +On Wed, 2016-08-17 at 08:42 -0700, Eric Dumazet wrote: +> On Wed, 2016-08-17 at 17:31 +0300, Denys Fedoryshchenko wrote: +> > Hi! +> > +> > Tried to run squid on latest kernel, and hit a panic +> > Sometimes it just shows warning in dmesg (but doesnt work properly) +> > [ 75.701666] IPv4: Attempt to release TCP socket in state 10 +> > ffff88102d430780 +> > [ 83.866974] squid (2700) used greatest stack depth: 12912 bytes left +> > [ 87.506644] IPv4: Attempt to release TCP socket in state 10 +> > ffff880078a48780 +> > [ 114.704295] IPv4: Attempt to release TCP socket in state 10 +> > ffff881029f8ad00 +> > +> > I cannot catch yet oops/panic message, netconsole not working. +> > +> > After triggering warning message 3 times, i am unable to run squid +> > anymore (without reboot), and in netstat it doesnt show port running. +> > +> > firewall is: +> > *mangle +> > -A PREROUTING -p tcp -m socket -j DIVERT +> > -A PREROUTING -p tcp -m tcp --dport 80 -i eno1 -j TPROXY --on-port 3129 +> > --on-ip 0.0.0.0 --tproxy-mark 0x1/0x1 +> > -A DIVERT -j MARK --set-xmark 0x1/0xffffffff +> > -A DIVERT -j ACCEPT +> > +> > routing +> > ip rule add fwmark 1 lookup 100 +> > ip route add local default dev eno1 table 100 +> > +> > +> > squid config is default with tproxy option +> > http_port 3129 tproxy +> > +> +> Hmppff... sorry for this, I will send a fix. +> +> Thanks for the report ! +> + + +Could you try the following ? + +Thanks ! + + net/netfilter/xt_TPROXY.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c +index 7f4414d26a66..663c4c3c9072 100644 +--- a/net/netfilter/xt_TPROXY.c ++++ b/net/netfilter/xt_TPROXY.c +@@ -127,6 +127,8 @@ nf_tproxy_get_sock_v4(struct net *net, struct sk_buff *skb, void *hp, + daddr, dport, + in->ifindex); + ++ if (sk && !atomic_inc_not_zero(&sk->sk_refcnt)) ++ sk = NULL; + /* NOTE: we return listeners even if bound to + * 0.0.0.0, those are filtered out in + * xt_socket, since xt_TPROXY needs 0 bound +@@ -195,6 +197,8 @@ nf_tproxy_get_sock_v6(struct net *net, struct sk_buff *skb, int thoff, void *hp, + daddr, ntohs(dport), + in->ifindex); + ++ if (sk && !atomic_inc_not_zero(&sk->sk_refcnt)) ++ sk = NULL; + /* NOTE: we return listeners even if bound to + * 0.0.0.0, those are filtered out in + * xt_socket, since xt_TPROXY needs 0 bound diff --git a/kernel.build.koji.epel7 b/kernel.build.koji.epel7 new file mode 100755 index 000000000..96af02882 --- /dev/null +++ b/kernel.build.koji.epel7 @@ -0,0 +1 @@ +time fedpkg --dist epel7 build --scratch --srpm --arches x86_64 i686 \ No newline at end of file diff --git a/kernel.build.koji.f25 b/kernel.build.koji.f25 new file mode 100755 index 000000000..16653741f --- /dev/null +++ b/kernel.build.koji.f25 @@ -0,0 +1 @@ +fedpkg --dist f25 build --scratch --srpm --arch=x86_64 \ No newline at end of file diff --git a/kernel.spec b/kernel.spec index b1b42cc03..d605dfff2 100644 --- a/kernel.spec +++ b/kernel.spec @@ -24,7 +24,7 @@ Summary: The Linux kernel %global zipsed -e 's/\.ko$/\.ko.xz/' %endif -# define buildid .local +%define buildid .pf7.hu.1 # baserelease defines which build revision of this kernel version we're # building. We used to call this fedora_build, but the magical name @@ -42,7 +42,7 @@ Summary: The Linux kernel # For non-released -rc kernels, this will be appended after the rcX and # gitX tags, so a 3 here would become part of release "0.rcX.gitX.3" # -%global baserelease 1 +%global baserelease 300 %global fedora_build %{baserelease} # base_sublevel is the kernel version we're starting with and patching @@ -54,7 +54,8 @@ Summary: The Linux kernel %if 0%{?released_kernel} # Do we have a -stable update to apply? -%define stable_update 0 +#+Hu Pf against 4.8.7 v4.8-pf6: https://pf.natalenko.name/news/?p=219 +%define stable_update 7 # Set rpm version accordingly %if 0%{?stable_update} %define stablerev %{stable_update} @@ -354,7 +355,10 @@ Summary: The Linux kernel Name: kernel%{?variant} Group: System Environment/Kernel License: GPLv2 and Redistributable, no modification permitted -URL: http://www.kernel.org/ +#URL: http://www.kernel.org/ +# Hubbitus patched fork of Fedora Kernel. Post-factum (https://pf.natalenko.name/) branch. +# Binaries could be found at: http://hubbitus.info/wiki/Repository +URL: https://github.com/Hubbitus/kernel/ Version: %{rpmversion} Release: %{pkg_release} # DO NOT CHANGE THE 'ExclusiveArch' LINE TO TEMPORARILY EXCLUDE AN ARCHITECTURE BUILD. @@ -467,7 +471,8 @@ Source2001: cpupower.config # For a stable release kernel %if 0%{?stable_update} %if 0%{?stable_base} -%define stable_patch_00 patch-4.%{base_sublevel}.%{stable_base}.xz +#*Hu %%define stable_patch_00 patch-4.%%{base_sublevel}.%%{stable_base}.xz +%global stable_patch_00 https://pf.natalenko.name/sources/4.8/patch-4.8-pf7.xz Source5000: %{stable_patch_00} %endif @@ -513,6 +518,14 @@ Patch425: arm64-pcie-quirks.patch # http://www.spinics.net/lists/linux-tegra/msg26029.html Patch426: usb-phy-tegra-Add-38.4MHz-clock-table-entry.patch +# Fix OMAP4 (pandaboard) +Patch427: arm-revert-mmc-omap_hsmmc-Use-dma_request_chan-for-reque.patch +Patch428: ARM-OMAP4-Fix-crashes.patch + +# Not particularly happy we don't yet have a proper upstream resolution this is the right direction +# https://www.spinics.net/lists/arm-kernel/msg535191.html +Patch429: arm64-mm-Fix-memmap-to-be-initialized-for-the-entire-section.patch + # http://patchwork.ozlabs.org/patch/587554/ Patch430: ARM-tegra-usb-no-reset.patch @@ -520,6 +533,8 @@ Patch431: bcm2837-initial-support.patch Patch432: bcm283x-vc4-fixes.patch +Patch433: AllWinner-net-emac.patch + Patch460: lib-cpumask-Make-CPUMASK_OFFSTACK-usable-without-deb.patch Patch466: input-kill-stupid-messages.patch @@ -615,14 +630,23 @@ Patch846: security-selinux-overlayfs-support.patch #rhbz 1360688 Patch847: rc-core-fix-repeat-events.patch -#rhbz 1374212 -Patch848: 0001-cpupower-Correct-return-type-of-cpu_power_is_cpu_onl.patch - #ongoing complaint, full discussion delayed until ksummit/plumbers Patch849: 0001-iio-Use-event-header-from-kernel-tree.patch -#CVE-2016-7425 rhbz 1377330 1377331 -Patch850: arcmsr-buffer-overflow-in-archmsr_iop_message_xfer.patch +# CVE-2016-9083 CVE-2016-9084 rhbz 1389258 1389259 1389285 +Patch850: v3-vfio-pci-Fix-integer-overflows-bitmask-check.patch + +#rhbz 1325354 +Patch852: 0001-HID-input-ignore-System-Control-application-usages-i.patch + +#rhbz 1392885 +Patch853: 0001-drm-i915-Refresh-that-status-of-MST-capable-connecto.patch + +#rhbz 1390308 +Patch854: nouveau-add-maxwell-to-backlight-init.patch + +#rhbz 1385823 +Patch855: 0001-platform-x86-ideapad-laptop-Add-Lenovo-Yoga-910-13IK.patch # END OF PATCH DEFINITIONS @@ -1005,14 +1029,16 @@ fi 2>/dev/null patch_command='patch -p1 -F1 -s' ApplyPatch() { - local patch=$1 +#Hu basename to allow use URLs in patches + local patch=$( basename $1 ) + local patchURL=$1 shift if [ ! -f $RPM_SOURCE_DIR/$patch ]; then exit 1 fi - if ! grep -E "^Patch[0-9]+: $patch\$" %{_specdir}/${RPM_PACKAGE_NAME%%%%%{?variant}}.spec ; then + if ! grep -E "^Patch[0-9]+: $patchURL\$" %{_specdir}/${RPM_PACKAGE_NAME%%%%%{?variant}}.spec ; then if [ "${patch:0:8}" != "patch-4." ] ; then - echo "ERROR: Patch $patch not listed as a source patch in specfile" + echo "ERROR: Patch [$patch] not listed as a source patch in specfile" exit 1 fi fi 2>/dev/null @@ -1189,6 +1215,8 @@ fi %if 0%{?stable_base} # This is special because the kernel spec is hell and nothing is consistent xzcat %{SOURCE5000} | patch -p1 -F1 -s +#+Hu: Place for manual hotfixes! +#patch -p1 < %%{SOURCE5002} git commit -a -m "Stable update" %endif @@ -1239,7 +1267,7 @@ touch .scmversion # only deal with configs if we are going to build for the arch %ifnarch %nobuildarches -mkdir configs +mkdir -p configs %if !%{debugbuildsenabled} rm -f kernel-%{version}-*debug.config @@ -1248,7 +1276,7 @@ rm -f kernel-%{version}-*debug.config %define make make %{?cross_opts} # now run oldconfig over all the config files -for i in *.config +for i in %{all_arch_configs} do mv $i .config Arch=`head -1 .config | cut -b 3-` @@ -1699,9 +1727,9 @@ chmod +x tools/power/cpupower/utils/version-gen.sh pushd tools/thermal/tmon/ %{make} popd -pushd tools/iio/ -%{make} -popd +#-Hu1 pushd tools/iio/ +#-Hu1 %{make} +#-Hu1 popd %endif # In the modsign case, we do 3 things. 1) We check the "flavour" and hard @@ -1869,9 +1897,9 @@ install -m644 %{SOURCE2001} %{buildroot}%{_sysconfdir}/sysconfig/cpupower pushd tools/thermal/tmon make INSTALL_ROOT=%{buildroot} install popd -pushd tools/iio -make INSTALL_ROOT=%{buildroot} install -popd +#-Hu1 pushd tools/iio +#-Hu1 make INSTALL_ROOT=%{buildroot} install +#-Hu1 popd %endif %if %{with_bootwrapper} @@ -2063,9 +2091,9 @@ fi %{_mandir}/man8/turbostat* %endif %{_bindir}/tmon -%{_bindir}/iio_event_monitor -%{_bindir}/iio_generic_buffer -%{_bindir}/lsiio +#-Hu1 %{_bindir}/iio_event_monitor +#-Hu1 %{_bindir}/iio_generic_buffer +#-Hu1 %{_bindir}/lsiio %endif %if %{with_debuginfo} @@ -2160,6 +2188,117 @@ fi # # %changelog +* Mon Nov 21 2016 Pavel Alexeev - 4.8.7-300.pf7.hu.1 +- Merge Fedora upstream changes. +- Update pf https://pf.natalenko.name/news/?p=219 to v4.8-pf7. + +* Mon Nov 21 2016 Justin M. Forbes - 4.8.10-300 +- Linux v4.8.10 + +* Sun Nov 20 2016 Peter Robinson +- Minor ARM config tweaks + +* Tue Nov 15 2016 Justin M. Forbes - 4.8.8-300 +- Linux v4.8.8 +- Fix crash in tcp_collapse CVE-2016-8645 (rhbz 1393904 1393908) + +* Mon Nov 14 2016 Laura Abbott +- Fix for some Yoga laptop WIFI (rhbz 1385823) + +* Fri Nov 11 2016 Justin M. Forbes +- Nouveau: Add Maxwell to backlight initialization (rhbz 1390308) + +* Thu Nov 10 2016 Justin M. Forbes - 4.8.7-300 +- Linux v4.8.7 +- Fixes cve-2016-8630 (rhbz 1393350 1393358) +- Refresh status of MST capable connectors (rhbz 1392885) + +* Wed Nov 09 2016 Pavel Alexeev - 4.8.6-301.pf6.hu.1 +- Rebase Fedora changes - kernel 4.8.6. +- Update pf patch to v4.8-pf6 - https://pf.natalenko.name/news/?p=217 + +* Wed Nov 2 2016 Justin M. Forbes - 4.8.6-301 +- dm raid: fix compat_features validation (rhbz 1391279) + +* Tue Nov 1 2016 Peter Robinson 4.8.6-300 +- Linux v4.8.6 +- Add revert to fix omap4 mmc (panda) +- Other minor omap4 fixes +- Adjust config for some AllWinner devices that don't like modular bits +- Add patch for aarch64 memory regions + +* Mon Oct 31 2016 Pavel Alexeev - 4.8.5-300.pf5.hu.3 +- CONFIG_SCHED_MUQSS=y + +* Mon Oct 31 2016 Pavel Alexeev - 4.8.5-300.pf5.hu.2 +- Try build with CONFIG_SCHED_MUQSS=n by suggestipon of Oleksandr Natalenko in mail. + +* Sun Oct 30 2016 Pavel Alexeev - 4.8.5-300.pf5.hu.1 +- Kernel 4.8.5. +- Pull Fedora changes. +- Update pf patch to v4.8-pf5 https://pf.natalenko.name/news/?p=213. + +* Sat Oct 29 2016 Peter Robinson +- Minor VC4 bug fix + +* Fri Oct 28 2016 Justin M. Forbes - 4.8.5-300 +- Linux v4.8.5 + +* Thu Oct 27 2016 Pavel Alexeev - 4.8.4-301.pf4.hu.1 +- Pull Fedora changes. Step to 4.8.4. +- Due to the error build on epel http://koji.fedoraproject.org/koji/getfile?taskID=16206974&name=build.log&offset=-4000 DISABLE build tools/iio! +- Upodate pf to 4.8-pf4 - https://pf.natalenko.name/news/?p=211. + +* Thu Oct 27 2016 Justin M. Forbes +- CVE-2016-9083 CVE-2016-9084 vfio multiple flaws (rhbz 1389258 1389259 1389285) +- Skylake i915 fixes from 4.9 +- Fix MS input devices identified as joysticks (rhbz 1325354) + +* Mon Oct 24 2016 Peter Robinson 4.8.4-301 +- Upstream fix for Raspberry Pi to fix setting low-resolution video modes on HDMI +- A collection of other clock fixes in -next for the RPi + +* Mon Oct 24 2016 Justin M. Forbes - 4.8.4-300 +- Linux v4.8.4 + +* Sat Oct 22 2016 Pavel Alexeev - 4.8.2-300.pf2.hu.1 +- Update to v4.8-pf2 - https://pf.natalenko.name/news/?p=207 + There BFS CPU scheduler has been replaced by its successor, MuQSS. Detailes: https://ck-hack.blogspot.de/2016/10/muqss-multiple-queue-skiplist-scheduler.html +- Change naming scheme to 4.8.0-300.pf2.hu.1 from 4.8.0-300.hu.1.pf2 + + +* Thu Oct 20 2016 Justin M. Forbes - 4.8.3-300 +- Linux v4.8.3 +- CVE-2016-5195 (rhbz 1384344 1387080) + +* Tue Oct 18 2016 Justin M. Forbes +- Fix memory corruption caused by p8_ghash +- Make __xfs_xattr_put_listen preperly report errors (rhbz 1384606) + +* Tue Oct 18 2016 Peter Robinson +- Disable ACPI_CPPC_CPUFREQ on aarch64 +- Add ethernet driver for AllWinner sun8i-emac (H3/OrangePi and Pine64) + +* Mon Oct 17 2016 Justin M. Forbes - 4.8.2-300 +- Linux v4.8.2 +- i8042 - skip selftest on ASUS laptops + +* Sat Oct 15 2016 Peter Robinson +- Build in AXP20X_I2C (should fix rhbz 1352140) + +* Tue Oct 11 2016 Pavel Alexeev - 4.8.0-1.hu.1.pf1 +- Step to build kernels for Fedora 25. +- Use new pf patch v4.8-pf1 - https://pf.natalenko.name/news/?p=204 + +* Fri Oct 07 2016 Justin M. Forbes +- update baserelease for Fedora 25 + +* Fri Oct 07 2016 Justin M. Forbes - 4.8.1-1 +- Linux v4.8.1 + +* Tue Oct 04 2016 Josh Boyer +- Add patch to fix Xorg starting with virtio (rhbz 1366842) + * Mon Oct 03 2016 Justin M. Forbes - 4.8.0-1 - Disable debugging options. - Linux v4.8 @@ -2201,6 +2340,13 @@ fi - Linux v4.8-rc7-37-gd2ffb01 - Reenable debugging options. +#* Tue Sep 20 2016 Pavel Alexeev - 4.7.4-200.hu.2.pf4 +#- Add patch1 http://ck.kolivas.org/patches/bfs/4.0/4.7/Pending/bfs497-build_other_arches.patch from Oleksand Natalenko + +#* Mon Sep 19 2016 Pavel Alexeev - 4.7.4-200.hu.1.pf4 +#- Merge Fedora upstream: kernel 4.7.4. +#- Update pf patch: 4.7-pf4 - https://pf.natalenko.name/news/?p=195 + * Mon Sep 19 2016 Justin M. Forbes - 4.8.0-0.rc7.git0.1 - Linux v4.8-rc7 - Disable debugging options. @@ -2958,6 +3104,11 @@ fi independent from each other - sign modules on all archs +#* Tue Mar 08 2016 Pavel Alexeev - 4.4.4-301.hu.1.pf6 +#- Merge Fedora changes. +#- Step to kernel 4.4.4. +#- Update pf patch: v4.4-pf6 - https://pf.natalenko.name/news/?p=161 + * Mon Mar 7 2016 Peter Robinson 4.5.0-0.rc7.git0.2 - Disble ARM_PATCH_IDIV as a work around to fix rhbz 1303147 @@ -2996,6 +3147,10 @@ fi - Fix tegra nouveau module load (thank kwizart for reference) - PowerPC Little Endian ToC fix +#* Mon Feb 22 2016 Pavel Alexeev - 4.4.2-300.hu.1.pf5 +#- Merge upstream changes. Step to 4.4.2! +#- Update pf patch to v4.4-pf5 + * Sun Feb 21 2016 Justin M. Forbes - 4.5.0-0.rc5.git0.1 - Disable debugging options. - Linux v4.5-rc5 @@ -3069,10 +3224,17 @@ fi * Wed Jan 27 2016 Justin M. Forbes - 4.5.0-0.rc1.git0.2 - Only apply KEY_FLAG_KEEP to a key if a parent keyring has it set (rhbz 1301099) +#* Tue Jan 26 2016 Pavel Alexeev - 4.3.3-303.hu.2.pf4 +#- While Fedora step to 4.3.4, pf is still 4.3.4. But merging Fedora patch changes. + * Mon Jan 25 2016 Justin M. Forbes - 4.5.0-0.rc1.git0.1 - Disable debugging options. - Linux v4.5-rc1 +#* Sat Jan 23 2016 Pavel Alexeev - 4.3.3-303.hu.1.pf4 +#- Merge Fedora 15 patches. +#- 4.3.3-303.hu.1.pf4 + * Fri Jan 22 2016 Justin M. Forbes - 4.5.0-0.rc0.git9.1 - Linux v4.4-10454-g3e1e21c @@ -3335,6 +3497,15 @@ fi - Disable Exynos IOMMU as it crashes - Minor ARMv7 update for battiery/charging +#* Sat Nov 07 2015 Pavel Alexeev - 4.2.5-300.hu.1.pf3 +#- Update to Fedora23. +#- Merge fc23 branch. +#- Adjust hibernate-Disable-in-a-signed-modules-environment.patch. + +#* Thu Nov 05 2015 Pavel Alexeev - 4.2.5-201.hu.1.pf3.fc22 +#- Update to pf3 - v4.2-pf3: https://pf.natalenko.name/forum/index.php?topic=363.0 +#- 4.2.5-201.hu.1.pf3 + * Thu Nov 05 2015 Laura Abbott - 4.4.0-0.rc0.git3.1 - Linux v4.3-6681-g8e483ed @@ -3514,6 +3685,10 @@ fi * Fri Sep 11 2015 Josh Boyer - 4.3.0-0.rc0.git12.1 - Linux v4.2-10963-g519f526d391b +#* Sun Sep 13 2015 Pavel Alexeev - 4.1.6-201.hu.1.pf4.fc22 +#- Update pf to v4.1-pf4 - https://pf.natalenko.name/forum/index.php?topic=345.0 +#- Possible kernel-3.19-bfs-compat-hubbitus.patch will not needed anymore (https://pf.natalenko.name/forum/index.php?topic=332.0). + * Wed Sep 09 2015 Josh Boyer - 4.3.0-0.rc0.git11.1 - Linux v4.2-10774-g26d2177e977c @@ -3656,6 +3831,9 @@ fi * Mon Aug 03 2015 Josh Boyer - Revert upstream commit 1c220c69ce to fix i686 booting (rhbz 1247382) +#* Sat Aug 01 2015 Pavel Alexeev - 4.1.2-200.hu.2.pf1 +#- Merge Fedora changes, but stay at 4.1.2 as PF patch is. + * Fri Jul 31 2015 Josh Boyer - 4.2.0-0.rc4.git4.1 - Linux v4.2-rc4-111-g8400935737bf @@ -3699,6 +3877,10 @@ fi * Mon Jul 20 2015 Josh Boyer - 4.2.0-0.rc3.git0.1 - Linux v4.2-rc3 +#* Sat Jul 18 2015 Pavel Alexeev - 4.1.2-200.hu.1.pf1.fc22 +#- Linux 4.1.2 +#- Update PF patch to v4.1-pf1 + * Fri Jul 17 2015 Josh Boyer - 4.2.0-0.rc2.git2.1 - Linux v4.2-rc2-190-g21bdb584af8c @@ -3708,6 +3890,9 @@ fi * Wed Jul 15 2015 Josh Boyer - 4.2.0-0.rc2.git1.1 - Linux v4.2-rc2-77-gf760b87f8f12 +* Wed Jul 15 2015 Josh Boyer +- Drop kdbus as it wasn't merged in time for f23 + * Tue Jul 14 2015 Peter Robinson - Update AMD Seattle a0 eth driver for 4.2 @@ -3744,6 +3929,9 @@ fi * Wed Jul 01 2015 Josh Boyer - 4.2.0-0.rc0.git4.1 - Linux v4.1-11549-g05a8256c586a +#* Tue Jun 30 2015 Pavel Alexeev - 4.0.5.hu.2.pf6.fc22 +#- Pf still against 4.0.5 v4.0-pf6: https://pf.natalenko.name/forum/index.php?topic=324, so just ne build to incorporate upstream fedora patches. + * Tue Jun 30 2015 Josh Boyer - 4.2.0-0.rc0.git3.1 - Linux v4.1-11355-g6aaf0da8728c - Add patch to fix KVM sleeping in atomic issue (rhbz 1237143) @@ -3778,6 +3966,10 @@ fi * Mon Jun 15 2015 Josh Boyer - 4.1.0-0.rc8.git0.1 - Linux v4.1-rc8 +#* Sun Jun 14 2015 Pavel Alexeev - 4.0.4-303.hu.1.pf6.fc22 +#- Upgrade to Fedora 22. Start f22-pf branch for kernels. First attempt build. Port changes from f21. +#- 4.0.4-303.hu.1.pf6 + * Fri Jun 12 2015 Josh Boyer - 4.1.0-0.rc7.git1.1 - Linux v4.1-rc7-72-gdf5f4158415b diff --git a/kvm-vmx-more-complete-state-update-on-APICv-on-off.patch b/kvm-vmx-more-complete-state-update-on-APICv-on-off.patch new file mode 100644 index 000000000..67043300c --- /dev/null +++ b/kvm-vmx-more-complete-state-update-on-APICv-on-off.patch @@ -0,0 +1,112 @@ +From: Roman Kagan +Subject: [PATCH v3] kvm:vmx: more complete state update on APICv on/off +Date: 2016-05-18 14:48:20 GMT (1 day, 21 hours and 23 minutes ago) + +The function to update APICv on/off state (in particular, to deactivate +it when enabling Hyper-V SynIC), used to be incomplete: it didn't adjust +APICv-related fields among secondary processor-based VM-execution +controls. + +As a result, Windows 2012 guests would get stuck when SynIC-based +auto-EOI interrupt intersected with e.g. an IPI in the guest. + +In addition, the MSR intercept bitmap wasn't updated to correspond to +whether "virtualize x2APIC mode" was enabled. This path used not to be +triggered, since Windows didn't use x2APIC but rather their own +synthetic APIC access MSRs; however it represented a security risk +because the guest running in a SynIC-enabled VM could switch to x2APIC +and thus obtain direct access to host APIC MSRs (thanks to Yang Zhang + for spotting this). + +The patch fixes those omissions. + +Signed-off-by: Roman Kagan +Cc: Steve Rutherford +Cc: Yang Zhang +--- +v2 -> v3: + - only switch to x2apic msr bitmap if virtualize x2apic mode is on in vmcs + +v1 -> v2: + - only update relevant bits in the secondary exec control + - update msr intercept bitmap (also make x2apic msr bitmap always + correspond to APICv) + + arch/x86/kvm/vmx.c | 48 ++++++++++++++++++++++++++++++------------------ + 1 file changed, 30 insertions(+), 18 deletions(-) + +diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c +index ee1c8a9..cef741a 100644 +--- a/arch/x86/kvm/vmx.c ++++ b/arch/x86/kvm/vmx.c +@@ -2418,7 +2418,9 @@ static void vmx_set_msr_bitmap(struct kvm_vcpu *vcpu) + + if (is_guest_mode(vcpu)) + msr_bitmap = vmx_msr_bitmap_nested; +- else if (vcpu->arch.apic_base & X2APIC_ENABLE) { ++ else if (cpu_has_secondary_exec_ctrls() && ++ (vmcs_read32(SECONDARY_VM_EXEC_CONTROL) & ++ SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE)) { + if (is_long_mode(vcpu)) + msr_bitmap = vmx_msr_bitmap_longmode_x2apic; + else +@@ -4783,6 +4785,19 @@ static void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) + struct vcpu_vmx *vmx = to_vmx(vcpu); + + vmcs_write32(PIN_BASED_VM_EXEC_CONTROL, vmx_pin_based_exec_ctrl(vmx)); ++ if (cpu_has_secondary_exec_ctrls()) { ++ if (kvm_vcpu_apicv_active(vcpu)) ++ vmcs_set_bits(SECONDARY_VM_EXEC_CONTROL, ++ SECONDARY_EXEC_APIC_REGISTER_VIRT | ++ SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); ++ else ++ vmcs_clear_bits(SECONDARY_VM_EXEC_CONTROL, ++ SECONDARY_EXEC_APIC_REGISTER_VIRT | ++ SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); ++ } ++ ++ if (cpu_has_vmx_msr_bitmap()) ++ vmx_set_msr_bitmap(vcpu); + } + + static u32 vmx_exec_control(struct vcpu_vmx *vmx) +@@ -6329,23 +6344,20 @@ static __init int hardware_setup(void) + + set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */ + +- if (enable_apicv) { +- for (msr = 0x800; msr <= 0x8ff; msr++) +- vmx_disable_intercept_msr_read_x2apic(msr); +- +- /* According SDM, in x2apic mode, the whole id reg is used. +- * But in KVM, it only use the highest eight bits. Need to +- * intercept it */ +- vmx_enable_intercept_msr_read_x2apic(0x802); +- /* TMCCT */ +- vmx_enable_intercept_msr_read_x2apic(0x839); +- /* TPR */ +- vmx_disable_intercept_msr_write_x2apic(0x808); +- /* EOI */ +- vmx_disable_intercept_msr_write_x2apic(0x80b); +- /* SELF-IPI */ +- vmx_disable_intercept_msr_write_x2apic(0x83f); +- } ++ for (msr = 0x800; msr <= 0x8ff; msr++) ++ vmx_disable_intercept_msr_read_x2apic(msr); ++ ++ /* According SDM, in x2apic mode, the whole id reg is used. But in ++ * KVM, it only use the highest eight bits. Need to intercept it */ ++ vmx_enable_intercept_msr_read_x2apic(0x802); ++ /* TMCCT */ ++ vmx_enable_intercept_msr_read_x2apic(0x839); ++ /* TPR */ ++ vmx_disable_intercept_msr_write_x2apic(0x808); ++ /* EOI */ ++ vmx_disable_intercept_msr_write_x2apic(0x80b); ++ /* SELF-IPI */ ++ vmx_disable_intercept_msr_write_x2apic(0x83f); + + if (enable_ept) { + kvm_mmu_set_mask_ptes(0ull, +-- +2.5.5 diff --git a/mm-thp-kvm-fix-memory-corruption-in-KVM-with-THP-ena.patch b/mm-thp-kvm-fix-memory-corruption-in-KVM-with-THP-ena.patch new file mode 100644 index 000000000..cc3e2168c --- /dev/null +++ b/mm-thp-kvm-fix-memory-corruption-in-KVM-with-THP-ena.patch @@ -0,0 +1,126 @@ +From 94f984ff563d1777652b822d7a282cacc1e481c2 Mon Sep 17 00:00:00 2001 +From: Andrea Arcangeli +Date: Wed, 27 Apr 2016 12:04:46 -0500 +Subject: [PATCH] mm: thp: kvm: fix memory corruption in KVM with THP enabled + +After the THP refcounting change, obtaining a compound pages from +get_user_pages() no longer allows us to assume the entire compound +page is immediately mappable from a secondary MMU. + +A secondary MMU doesn't want to call get_user_pages() more than once +for each compound page, in order to know if it can map the whole +compound page. So a secondary MMU needs to know from a single +get_user_pages() invocation when it can map immediately the entire +compound page to avoid a flood of unnecessary secondary MMU faults and +spurious atomic_inc()/atomic_dec() (pages don't have to be pinned by +MMU notifier users). + +Ideally instead of the page->_mapcount < 1 check, get_user_pages() +should return the granularity of the "page" mapping in the "mm" passed +to get_user_pages(). However it's non trivial change to pass the "pmd" +status belonging to the "mm" walked by get_user_pages up the stack (up +to the caller of get_user_pages). So the fix just checks if there is +not a single pte mapping on the page returned by get_user_pages, and +in turn if the caller can assume that the whole compound page is +mapped in the current "mm" (in a pmd_trans_huge()). In such case the +entire compound page is safe to map into the secondary MMU without +additional get_user_pages() calls on the surrounding tail/head +pages. In addition of being faster, not having to run other +get_user_pages() calls also reduces the memory footprint of the +secondary MMU fault in case the pmd split happened as result of memory +pressure. + +Without this fix after a MADV_DONTNEED (like invoked by QEMU during +postcopy live migration or balloning) or after generic swapping (with +a failure in split_huge_page() that would only result in pmd splitting +and not a physical page split), KVM would map the whole compound page +into the shadow pagetables, despite regular faults or userfaults (like +UFFDIO_COPY) may map regular pages into the primary MMU as result of +the pte faults, leading to the guest mode and userland mode going out +of sync and not working on the same memory at all times. + +Signed-off-by: Andrea Arcangeli +--- + arch/arm/kvm/mmu.c | 2 +- + arch/x86/kvm/mmu.c | 4 ++-- + include/linux/page-flags.h | 22 ++++++++++++++++++++++ + 3 files changed, 25 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c +index aba61fd..8dafe97 100644 +--- a/arch/arm/kvm/mmu.c ++++ b/arch/arm/kvm/mmu.c +@@ -997,7 +997,7 @@ static bool transparent_hugepage_adjust(kvm_pfn_t *pfnp, phys_addr_t *ipap) + kvm_pfn_t pfn = *pfnp; + gfn_t gfn = *ipap >> PAGE_SHIFT; + +- if (PageTransCompound(pfn_to_page(pfn))) { ++ if (PageTransCompoundMap(pfn_to_page(pfn))) { + unsigned long mask; + /* + * The address we faulted on is backed by a transparent huge +diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c +index 1e7a49b..3a371f7 100644 +--- a/arch/x86/kvm/mmu.c ++++ b/arch/x86/kvm/mmu.c +@@ -2767,7 +2767,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu, + */ + if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) && + level == PT_PAGE_TABLE_LEVEL && +- PageTransCompound(pfn_to_page(pfn)) && ++ PageTransCompoundMap(pfn_to_page(pfn)) && + !has_wrprotected_page(vcpu, gfn, PT_DIRECTORY_LEVEL)) { + unsigned long mask; + /* +@@ -4621,7 +4621,7 @@ restart: + */ + if (sp->role.direct && + !kvm_is_reserved_pfn(pfn) && +- PageTransCompound(pfn_to_page(pfn))) { ++ PageTransCompoundMap(pfn_to_page(pfn))) { + drop_spte(kvm, sptep); + need_tlb_flush = 1; + goto restart; +diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h +index 19724e6..522bd6d 100644 +--- a/include/linux/page-flags.h ++++ b/include/linux/page-flags.h +@@ -517,6 +517,27 @@ static inline int PageTransCompound(struct page *page) + } + + /* ++ * PageTransCompoundMap is the same as PageTransCompound, but it also ++ * guarantees the primary MMU has the entire compound page mapped ++ * through pmd_trans_huge, which in turn guarantees the secondary MMUs ++ * can also map the entire compound page. This allows the secondary ++ * MMUs to call get_user_pages() only once for each compound page and ++ * to immediately map the entire compound page with a single secondary ++ * MMU fault. If there will be a pmd split later, the secondary MMUs ++ * will get an update through the MMU notifier invalidation through ++ * split_huge_pmd(). ++ * ++ * Unlike PageTransCompound, this is safe to be called only while ++ * split_huge_pmd() cannot run from under us, like if protected by the ++ * MMU notifier, otherwise it may result in page->_mapcount < 0 false ++ * positives. ++ */ ++static inline int PageTransCompoundMap(struct page *page) ++{ ++ return PageTransCompound(page) && atomic_read(&page->_mapcount) < 0; ++} ++ ++/* + * PageTransTail returns true for both transparent huge pages + * and hugetlbfs pages, so it should only be called when it's known + * that hugetlbfs pages aren't involved. +@@ -559,6 +580,7 @@ static inline int TestClearPageDoubleMap(struct page *page) + #else + TESTPAGEFLAG_FALSE(TransHuge) + TESTPAGEFLAG_FALSE(TransCompound) ++TESTPAGEFLAG_FALSE(TransCompoundMap) + TESTPAGEFLAG_FALSE(TransTail) + TESTPAGEFLAG_FALSE(DoubleMap) + TESTSETFLAG_FALSE(DoubleMap) +-- +2.7.4 + diff --git a/net-inet-fix-race-in-reqsk_queue_unlink.patch b/net-inet-fix-race-in-reqsk_queue_unlink.patch new file mode 100644 index 000000000..744084314 --- /dev/null +++ b/net-inet-fix-race-in-reqsk_queue_unlink.patch @@ -0,0 +1,76 @@ +From patchwork Thu Oct 1 12:39:26 2015 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [net] inet: fix race in reqsk_queue_unlink() +From: Eric Dumazet +X-Patchwork-Id: 524966 +Message-Id: <1443703166.32531.47.camel@edumazet-glaptop2.roam.corp.google.com> +To: David Miller +Cc: netdev , Yuchung Cheng +Date: Thu, 01 Oct 2015 05:39:26 -0700 + +From: Eric Dumazet + +reqsk_timer_handler() tests if icsk_accept_queue.listen_opt +is NULL at its beginning. + +By the time it calls inet_csk_reqsk_queue_drop() and +reqsk_queue_unlink(), listener might have been closed and +inet_csk_listen_stop() had called reqsk_queue_yank_acceptq() +which sets icsk_accept_queue.listen_opt to NULL + +We therefore need to correctly check listen_opt being NULL +after holding syn_wait_lock for proper synchronization. + +Fixes: fa76ce7328b2 ("inet: get rid of central tcp/dccp listener timer") +Fixes: b357a364c57c ("inet: fix possible panic in reqsk_queue_unlink()") +Signed-off-by: Eric Dumazet +Cc: Yuchung Cheng +--- + net/ipv4/inet_connection_sock.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + + + +-- +To unsubscribe from this list: send the line "unsubscribe netdev" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html + +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index 7bb9c39e0a4d..61b45a17fc73 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -577,21 +577,22 @@ EXPORT_SYMBOL(inet_rtx_syn_ack); + static bool reqsk_queue_unlink(struct request_sock_queue *queue, + struct request_sock *req) + { +- struct listen_sock *lopt = queue->listen_opt; + struct request_sock **prev; ++ struct listen_sock *lopt; + bool found = false; + + spin_lock(&queue->syn_wait_lock); +- +- for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL; +- prev = &(*prev)->dl_next) { +- if (*prev == req) { +- *prev = req->dl_next; +- found = true; +- break; ++ lopt = queue->listen_opt; ++ if (lopt) { ++ for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL; ++ prev = &(*prev)->dl_next) { ++ if (*prev == req) { ++ *prev = req->dl_next; ++ found = true; ++ break; ++ } + } + } +- + spin_unlock(&queue->syn_wait_lock); + if (timer_pending(&req->rsk_timer) && del_timer_sync(&req->rsk_timer)) + reqsk_put(req); diff --git a/netfilter-x_tables-check-for-size-overflow.patch b/netfilter-x_tables-check-for-size-overflow.patch new file mode 100644 index 000000000..81e3d36fa --- /dev/null +++ b/netfilter-x_tables-check-for-size-overflow.patch @@ -0,0 +1,31 @@ +Subject: [PATCH nf] netfilter: x_tables: check for size overflow +From: Florian Westphal +Date: 2016-03-10 0:56:23 + +Ben Hawkes says: + integer overflow in xt_alloc_table_info, which on 32-bit systems can + lead to small structure allocation and a copy_from_user based heap + corruption. + +Reported-by: Ben Hawkes +Signed-off-by: Florian Westphal +--- + net/netfilter/x_tables.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index c8a0b7d..17a9a9f 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -659,6 +659,9 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size) + struct xt_table_info *info = NULL; + size_t sz = sizeof(*info) + size; + ++ if (sz < sizeof(*info)) ++ return NULL; ++ + /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ + if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > totalram_pages) + return NULL; +-- +2.4.10 diff --git a/nouveau-add-maxwell-to-backlight-init.patch b/nouveau-add-maxwell-to-backlight-init.patch new file mode 100644 index 000000000..9d89069c1 --- /dev/null +++ b/nouveau-add-maxwell-to-backlight-init.patch @@ -0,0 +1,24 @@ +From bbe1f94a8b3f2e8622dd400a6827d3242005d951 Mon Sep 17 00:00:00 2001 +From: Faris Alsalama +Date: Sat, 21 May 2016 14:41:43 -0400 +Subject: drm/nouveau/kms: add Maxwell to backlight initialization + +Signed-off-by: Faris Alsalama +Acked-by: Acked-by: Pierre Moreau +Signed-off-by: Ben Skeggs + +diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c +index f5101be..5e2c568 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_backlight.c ++++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c +@@ -232,6 +232,7 @@ nouveau_backlight_init(struct drm_device *dev) + case NV_DEVICE_INFO_V0_TESLA: + case NV_DEVICE_INFO_V0_FERMI: + case NV_DEVICE_INFO_V0_KEPLER: ++ case NV_DEVICE_INFO_V0_MAXWELL: + return nv50_backlight_init(connector); + default: + break; +-- +cgit v0.10.2 + diff --git a/nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch b/nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch new file mode 100644 index 000000000..a6996faa1 --- /dev/null +++ b/nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch @@ -0,0 +1,33 @@ +From 16f4f5f0dc33698b841bce71cbc0dd991935b7e1 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 23 Jul 2015 17:20:12 +0200 +Subject: [PATCH] nv46: Change mc subdev oclass from nv44 to nv4c + +MSI interrupts appear to not work for nv46 based cards. Change the mc +subdev oclass for these cards from nv44 to nv4c, the nv4c mc code is +identical to the nv44 mc code except that it does not use msi +(it does not define a msi_rearm callback). + +BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=90435 +Signed-off-by: Hans de Goede +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c +index c6301361d14f..b4ad791b4851 100644 +--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c ++++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/nv40.c +@@ -265,7 +265,7 @@ nv40_identify(struct nvkm_device *device) + device->oclass[NVDEV_SUBDEV_CLK ] = &nv40_clk_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass; +- device->oclass[NVDEV_SUBDEV_MC ] = nv44_mc_oclass; ++ device->oclass[NVDEV_SUBDEV_MC ] = nv4c_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nv31_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nv46_fb_oclass; +-- +2.4.3 + diff --git a/ovl-fix-permission-checking-for-setattr.patch b/ovl-fix-permission-checking-for-setattr.patch new file mode 100644 index 000000000..167ecda99 --- /dev/null +++ b/ovl-fix-permission-checking-for-setattr.patch @@ -0,0 +1,46 @@ +From acff81ec2c79492b180fade3c2894425cd35a545 Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Fri, 4 Dec 2015 19:18:48 +0100 +Subject: [PATCH] ovl: fix permission checking for setattr + +[Al Viro] The bug is in being too enthusiastic about optimizing ->setattr() +away - instead of "copy verbatim with metadata" + "chmod/chown/utimes" +(with the former being always safe and the latter failing in case of +insufficient permissions) it tries to combine these two. Note that copyup +itself will have to do ->setattr() anyway; _that_ is where the elevated +capabilities are right. Having these two ->setattr() (one to set verbatim +copy of metadata, another to do what overlayfs ->setattr() had been asked +to do in the first place) combined is where it breaks. + +Signed-off-by: Miklos Szeredi +Cc: +Signed-off-by: Al Viro +--- + fs/overlayfs/inode.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c +index ec0c2a050043..961284936917 100644 +--- a/fs/overlayfs/inode.c ++++ b/fs/overlayfs/inode.c +@@ -49,13 +49,13 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) + if (err) + goto out; + +- upperdentry = ovl_dentry_upper(dentry); +- if (upperdentry) { ++ err = ovl_copy_up(dentry); ++ if (!err) { ++ upperdentry = ovl_dentry_upper(dentry); ++ + mutex_lock(&upperdentry->d_inode->i_mutex); + err = notify_change(upperdentry, attr, NULL); + mutex_unlock(&upperdentry->d_inode->i_mutex); +- } else { +- err = ovl_copy_up_last(dentry, attr, false); + } + ovl_drop_write(dentry); + out: +-- +2.5.0 + diff --git a/patch-4.8-pf7.xz b/patch-4.8-pf7.xz new file mode 100644 index 000000000..caead07e7 Binary files /dev/null and b/patch-4.8-pf7.xz differ diff --git a/rds-fix-an-infoleak-in-rds_inc_info_copy.txt b/rds-fix-an-infoleak-in-rds_inc_info_copy.txt new file mode 100644 index 000000000..a9b1e49fe --- /dev/null +++ b/rds-fix-an-infoleak-in-rds_inc_info_copy.txt @@ -0,0 +1,31 @@ +From 4116def2337991b39919f3b448326e21c40e0dbb Mon Sep 17 00:00:00 2001 +From: Kangjie Lu +Date: Thu, 2 Jun 2016 04:11:20 -0400 +Subject: rds: fix an infoleak in rds_inc_info_copy + +The last field "flags" of object "minfo" is not initialized. +Copying this object out may leak kernel stack data. +Assign 0 to it to avoid leak. + +Signed-off-by: Kangjie Lu +Acked-by: Santosh Shilimkar +Signed-off-by: David S. Miller +--- + net/rds/recv.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/rds/recv.c b/net/rds/recv.c +index c0be1ec..8413f6c 100644 +--- a/net/rds/recv.c ++++ b/net/rds/recv.c +@@ -561,5 +561,7 @@ void rds_inc_info_copy(struct rds_incoming *inc, + minfo.fport = inc->i_hdr.h_dport; + } + ++ minfo.flags = 0; ++ + rds_info_copy(iter, &minfo, sizeof(minfo)); + } +-- +cgit v0.12 + diff --git a/regulator-axp20x-module-alias.patch b/regulator-axp20x-module-alias.patch new file mode 100644 index 000000000..f20c9b3d6 --- /dev/null +++ b/regulator-axp20x-module-alias.patch @@ -0,0 +1,26 @@ +From d4ea7d86457a8d0ea40ce77bdeda1fc966cc35ec Mon Sep 17 00:00:00 2001 +From: Ian Campbell +Date: Sat, 1 Aug 2015 18:13:25 +0100 +Subject: regulator: axp20x: Add module alias + +This allows the module to be autoloaded. + +Together with 07949bf9c63c ("cpufreq: dt: allow driver to boot +automatically") this is sufficient to allow a modular kernel (such +as Debian's) to enable cpufreq on a Cubietruck. + +Signed-off-by: Ian Campbell +Signed-off-by: Mark Brown + +diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c +index 6468291..01bf347 100644 +--- a/drivers/regulator/axp20x-regulator.c ++++ b/drivers/regulator/axp20x-regulator.c +@@ -405,3 +405,4 @@ module_platform_driver(axp20x_regulator_driver); + MODULE_LICENSE("GPL v2"); + MODULE_AUTHOR("Carlo Caione "); + MODULE_DESCRIPTION("Regulator Driver for AXP20X PMIC"); ++MODULE_ALIAS("platform:axp20x-regulator"); +-- +cgit v0.10.2 + diff --git a/revert-stmmac-Fix-eth0-No-PHY-found-regression.patch b/revert-stmmac-Fix-eth0-No-PHY-found-regression.patch new file mode 100644 index 000000000..68b9cd3ab --- /dev/null +++ b/revert-stmmac-Fix-eth0-No-PHY-found-regression.patch @@ -0,0 +1,87 @@ +From 44f947bb8ef5f4add9f2d84e1ff53afd8f2f5537 Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Wed, 16 Mar 2016 15:21:44 +0000 +Subject: [PATCH 1/2] Revert "stmmac: Fix 'eth0: No PHY found' regression" + +This reverts commit 88f8b1bb41c6208f81b6a480244533ded7b59493. +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 11 ++++++++++- + drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 9 +-------- + include/linux/stmmac.h | 1 - + 3 files changed, 11 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +index efb54f3..0faf163 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +@@ -199,12 +199,21 @@ int stmmac_mdio_register(struct net_device *ndev) + struct stmmac_priv *priv = netdev_priv(ndev); + struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data; + int addr, found; +- struct device_node *mdio_node = priv->plat->mdio_node; ++ struct device_node *mdio_node = NULL; ++ struct device_node *child_node = NULL; + + if (!mdio_bus_data) + return 0; + + if (IS_ENABLED(CONFIG_OF)) { ++ for_each_child_of_node(priv->device->of_node, child_node) { ++ if (of_device_is_compatible(child_node, ++ "snps,dwmac-mdio")) { ++ mdio_node = child_node; ++ break; ++ } ++ } ++ + if (mdio_node) { + netdev_dbg(ndev, "FOUND MDIO subnode\n"); + } else { +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +index 4514ba7..6a52fa1 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -110,7 +110,6 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) + struct device_node *np = pdev->dev.of_node; + struct plat_stmmacenet_data *plat; + struct stmmac_dma_cfg *dma_cfg; +- struct device_node *child_node = NULL; + + plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); + if (!plat) +@@ -141,19 +140,13 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) + plat->phy_node = of_node_get(np); + } + +- for_each_child_of_node(np, child_node) +- if (of_device_is_compatible(child_node, "snps,dwmac-mdio")) { +- plat->mdio_node = child_node; +- break; +- } +- + /* "snps,phy-addr" is not a standard property. Mark it as deprecated + * and warn of its use. Remove this when phy node support is added. + */ + if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0) + dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n"); + +- if ((plat->phy_node && !of_phy_is_fixed_link(np)) || !plat->mdio_node) ++ if ((plat->phy_node && !of_phy_is_fixed_link(np)) || plat->phy_bus_name) + plat->mdio_bus_data = NULL; + else + plat->mdio_bus_data = +diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h +index 881a79d..eead8ab 100644 +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -100,7 +100,6 @@ struct plat_stmmacenet_data { + int interface; + struct stmmac_mdio_bus_data *mdio_bus_data; + struct device_node *phy_node; +- struct device_node *mdio_node; + struct stmmac_dma_cfg *dma_cfg; + int clk_csr; + int has_gmac; +-- +2.5.0 + diff --git a/scripts/add-changelog.sh b/scripts/add-changelog.sh new file mode 100755 index 000000000..fa0157afb --- /dev/null +++ b/scripts/add-changelog.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# Emulate the changelog part of rpmdev-bumpspec without the bumping of the +# rev. Because Laura keeps typoing her name and the date. + +CURDATE=`date +"%a %b %d %Y"` +PACKAGER=`rpmdev-packager` +CHANGELOG="%changelog\n* $CURDATE $PACKAGER\n- $1\n" + +awk -v CHANGE="$CHANGELOG" '/%changelog/ {print CHANGE} \ + !/%changelog/ { print $0 }' \ + < kernel.spec > kernel.spec.tmp +mv kernel.spec.tmp kernel.spec diff --git a/scripts/fixup-bumpspec.sh b/scripts/fixup-bumpspec.sh new file mode 100755 index 000000000..1a38de222 --- /dev/null +++ b/scripts/fixup-bumpspec.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# rpmdev-bumpspec 'helpfully' bumps the release which we don't always want. +# This script fixes it up. + +RELEASE=`grep "%global baserelease" kernel.spec | cut -d ' ' -f 3` +export RELEASE=$(($RELEASE-1)) +perl -p -i -e 's|%global baserelease.*|%global baserelease $ENV{'RELEASE'}|' kernel.spec +TODAY=`date +"%a %b %d %Y"` +awk -v DATE="$TODAY" 'START { marked = 0; } $0 ~ DATE { if (marked == 1) { print $0 } else {out=$1; for(i = 2; i <= NF - 2; i++) { out=out" "$i } print out; marked = 1; } } $0 !~ DATE { print $0; }' < kernel.spec > kernel.spec.tmp +mv kernel.spec.tmp kernel.spec diff --git a/scripts/kernel-version.sh b/scripts/kernel-version.sh new file mode 100644 index 000000000..36e00194b --- /dev/null +++ b/scripts/kernel-version.sh @@ -0,0 +1,8 @@ +VER=$(grep patch sources | head -n1 | awk '{ print $2 }' | sed s/patch-// | sed s/-git.*// | sed s/.xz//) + +if [ -z "$VER" ] ; +then + VER=$(grep linux sources | head -1 | awk '{ print $2 }' | sed s/linux-// | sed s/.tar.xz//) +fi + + diff --git a/scripts/rawhide-rc.sh b/scripts/rawhide-rc.sh new file mode 100755 index 000000000..2e871ee8e --- /dev/null +++ b/scripts/rawhide-rc.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# Generate a commit for a rawhide RC release + +source scripts/kernel-version.sh + +make release +# fixup the release because rpmdev-bumpspec *sigh* +scripts/fixup-bumpspec.sh +fedpkg commit -c + +# Figure out what is our RC +RC=`grep "%define rcrev" kernel.spec| cut -d ' ' -f 3` +RC=$(($RC+1)) +BASE=`grep "%define base_sublevel" kernel.spec| cut -d ' ' -f 3` + +# Kill all patches +awk '!/patch/ { print $0 }' < sources > sources.tmp +mv sources.tmp sources + +# Grab the tarball +if [ ! -f patch-4.$BASE-rc$RC.xz ]; then + wget https://cdn.kernel.org/pub/linux/kernel/v4.x/testing/patch-4.$BASE-rc$RC.xz + if [ ! $? -eq 0 ]; then + exit 1 + fi + fedpkg upload patch-4.$BASE-rc$RC.xz +fi + +# bump rcrev in the spec and set git snapshot to 0 +RC=$RC perl -p -i -e 's|%define rcrev.*|%global rcrev $ENV{'RC'}|' kernel.spec + +perl -p -i -e 's|%define gitrev.*|%define gitrev 0|' kernel.spec + +perl -p -i -e 's|%global baserelease.*|%global baserelease 0|' kernel.spec + +rpmdev-bumpspec -c "Linux v4.$BASE-rc$RC" kernel.spec diff --git a/scripts/rawhide-snapshot.sh b/scripts/rawhide-snapshot.sh new file mode 100755 index 000000000..b96bd38d9 --- /dev/null +++ b/scripts/rawhide-snapshot.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# A coffeeproof rawhide script. You should be able to run this before the +# coffee has kicked in and generate a good rawhide commit. +# +# - Updates the local Fedora tree to master and verifies that you are working +# off of the correct master +# - Updates the upstream tree to the latest master. +# - Generates a git snapshot via generate-git-snapshot.sh +# - Clears out old git snapshots from the sources +# - Uploads the new snapshot + +source scripts/kernel-version.sh + +git fetch origin +if [ "$(git rev-parse origin/master)" != "$(git rev-parse HEAD)" ]; then + echo "I just did a git fetch and this branch does not match master" + echo "Re-check out this branch to work off of the latest master" + exit 1 +fi + +if [ ! -d "$LINUX_GIT" ]; then + echo "error: set \$LINUX_GIT to point at an upstream git tree" + exit 1 +fi + +git -C $LINUX_GIT pull +if [ ! $? -eq 0 ]; then + echo "Git pull failed. Is your tree clean/correct?" + exit 1 +fi + +git -C $LINUX_GIT describe --tags HEAD | grep -q "\-g" +if [ ! $? -eq 0 ]; then + echo "Trying to snapshot off of a tagged git." + echo "I don't think this is what you want" + exit 1 +fi + +if [ "$(git -C $LINUX_GIT rev-parse origin/master)" == `cat gitrev` ]; then + echo "Last snapshot commit matches current master. Nothing to do" + echo "\o/" + exit 0 +fi + +GIT=`grep "%define gitrev" kernel.spec | cut -d ' ' -f 3` +if [ "$GIT" -eq 0 ]; then + make debug + ./scripts/fixup-bumpspec.sh + fedpkg commit -c +fi + +./scripts/generate-git-snapshot.sh + +#Nuke the old patch from the source +awk '!/git/ { print $0 }' < sources > sources.tmp +mv sources.tmp sources + +GIT=`grep "%define gitrev" kernel.spec | cut -d ' ' -f 3` +fedpkg upload patch-$VER-git$GIT.xz diff --git a/scripts/stable-update.sh b/scripts/stable-update.sh new file mode 100755 index 000000000..eefd9a96d --- /dev/null +++ b/scripts/stable-update.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# +# Author: Laura Abbott +# +# Apply a stable patch update to the Fedora tree. This takes care of +# - Downloading the patch from kernel.org +# - Uploading the source file +# - Removing old patch files +# - Updating the spec file stable version +# - Adding a proper changelog entry +# +# Based on steps from https://fedoraproject.org/wiki/Kernel/DayToDay#Stable_kernel_update +# +# Args: Stable version to update (e.g. 4.7.7, 4.8.1) + +if [ $# -lt 1 ]; then + echo "Need a version" + exit 1 +fi + +VERSION=`echo $1 | cut -d . -f 1` +if [ -z $VERSION ]; then + echo "Malformed version $1" + exit 1 +fi +PATCHLEVEL=`echo $1 | cut -d . -f 2` +if [ -z $VERSION ]; then + echo "Malformed version $1" + exit 1 +fi +SUBLEVEL=`echo $1 | cut -d . -f 3` +if [ -z $VERSION ]; then + echo "Malformed version $1" + exit 1 +fi + +if [ ! -f patch-$1.xz ]; then + wget https://cdn.kernel.org/pub/linux/kernel/v4.x/patch-$1.xz + if [ ! $? -eq 0 ]; then + echo "Download fail" + exit 1 + fi +fi + +grep $1 sources &> /dev/null +if [ ! $? -eq 0 ]; then + fedpkg upload patch-$1.xz + + # Cryptic awk: search for the previous patch level (if one exists) and + # remove it from the source file + awk -v VER=$VERSION.$PATCHLEVEL.$((SUBLEVEL-1)) '$0 !~ VER { print $0; }' < sources > sources.tmp + mv sources.tmp sources +fi + +# Update the stable level +awk -v STABLE=$SUBLEVEL '/%define stable_update/ \ + { print "%define stable_update " STABLE } \ + !/%define stable_update/ { print $0 }' \ + < kernel.spec > kernel.spec.tmp +mv kernel.spec.tmp kernel.spec + +# Reset the base release for use with rpmdev-bumpspec +BASERELEASE=`cat kernel.spec | grep "%global baserelease" | cut -d ' ' -f 3 | head -c 1`00 +BASERELEASE=$(($BASERELEASE-1)) +BASERELEASE=$BASERELEASE perl -p -i -e 's|%global baserelease.*|%global baserelease $ENV{'BASERELEASE'}|' kernel.spec + +rpmdev-bumpspec -c "Linux v$1" kernel.spec diff --git a/si2157-Bounds-check-firmware.patch b/si2157-Bounds-check-firmware.patch new file mode 100644 index 000000000..284006160 --- /dev/null +++ b/si2157-Bounds-check-firmware.patch @@ -0,0 +1,39 @@ +From 526fbce5b0e44c67a97c57656b3be9911f0a9b9b Mon Sep 17 00:00:00 2001 +From: Laura Abbott +Date: Tue, 29 Sep 2015 16:59:20 -0700 +Subject: [PATCH 2/2] si2157: Bounds check firmware +To: Antti Palosaari +To: Mauro Carvalho Chehab +Cc: Olli Salonen +Cc: linux-media@vger.kernel.org +Cc: linux-kernel@vger.kernel.org + +When reading the firmware and sending commands, the length +must be bounds checked to avoid overrunning the size of the command +buffer and smashing the stack if the firmware is not in the +expected format. Add the proper check. + +Cc: stable@kernel.org +Signed-off-by: Laura Abbott +--- + drivers/media/tuners/si2157.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c +index 5073821..ce157ed 100644 +--- a/drivers/media/tuners/si2157.c ++++ b/drivers/media/tuners/si2157.c +@@ -166,6 +166,10 @@ static int si2157_init(struct dvb_frontend *fe) + + for (remaining = fw->size; remaining > 0; remaining -= 17) { + len = fw->data[fw->size - remaining]; ++ if (len > SI2157_ARGLEN) { ++ dev_err(&client->dev, "Bad firmware length\n"); ++ goto err_release_firmware; ++ } + memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); + cmd.wlen = len; + cmd.rlen = 1; +-- +2.4.3 + diff --git a/si2168-Bounds-check-firmware.patch b/si2168-Bounds-check-firmware.patch new file mode 100644 index 000000000..e9c5bcc50 --- /dev/null +++ b/si2168-Bounds-check-firmware.patch @@ -0,0 +1,50 @@ +From 43018528944fa4965a4048fee91d76b47dcaf60e Mon Sep 17 00:00:00 2001 +From: Laura Abbott +Date: Mon, 28 Sep 2015 14:10:34 -0700 +Subject: [PATCH 1/2] si2168: Bounds check firmware +To: Antti Palosaari +To: Mauro Carvalho Chehab +Cc: Olli Salonen +Cc: linux-media@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Cc: Stuart Auchterlonie + + +When reading the firmware and sending commands, the length must +be bounds checked to avoid overrunning the size of the command +buffer and smashing the stack if the firmware is not in the expected +format: + +si2168 11-0064: found a 'Silicon Labs Si2168-B40' +si2168 11-0064: downloading firmware from file 'dvb-demod-si2168-b40-01.fw' +si2168 11-0064: firmware download failed -95 +Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: ffffffffa085708f + +Add the proper check. + +Cc: stable@kernel.org +Reported-by: Stuart Auchterlonie +Reviewed-by: Antti Palosaari +Signed-off-by: Laura Abbott +--- + drivers/media/dvb-frontends/si2168.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c +index 81788c5..821a8f4 100644 +--- a/drivers/media/dvb-frontends/si2168.c ++++ b/drivers/media/dvb-frontends/si2168.c +@@ -502,6 +502,10 @@ static int si2168_init(struct dvb_frontend *fe) + /* firmware is in the new format */ + for (remaining = fw->size; remaining > 0; remaining -= 17) { + len = fw->data[fw->size - remaining]; ++ if (len > SI2168_ARGLEN) { ++ ret = -EINVAL; ++ break; ++ } + memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); + cmd.wlen = len; + cmd.rlen = 1; +-- +2.4.3 + diff --git a/sources b/sources index 56f12af66..7e081ec62 100644 --- a/sources +++ b/sources @@ -1,2 +1,3 @@ c1af0afbd3df35c1ccdc7a5118cd2d07 linux-4.8.tar.xz 0dad03f586e835d538d3e0d2cbdb9a28 perf-man-4.8.tar.gz +37eadcdaefae51ced736747cb817aa58 patch-4.8.10.xz diff --git a/stmmac-fix-MDIO-settings.patch b/stmmac-fix-MDIO-settings.patch new file mode 100644 index 000000000..41fa928a4 --- /dev/null +++ b/stmmac-fix-MDIO-settings.patch @@ -0,0 +1,235 @@ +From d55a02f460ffd64a5ba7f331489af87edeebf8da Mon Sep 17 00:00:00 2001 +From: Giuseppe CAVALLARO +Date: Wed, 16 Mar 2016 10:38:49 +0100 +Subject: [PATCH 2/2] stmmac: fix MDIO settings +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Initially the phy_bus_name was added to manipulate the +driver name but it was recently just used to manage the +fixed-link and then to take some decision at run-time. +So the patch uses the is_pseudo_fixed_link and removes +the phy_bus_name variable not necessary anymore. + +The driver can manage the mdio registration by using phy-handle, +dwmac-mdio and own parameter e.g. snps,phy-addr. +This patch takes care about all these possible configurations +and fixes the mdio registration in case of there is a real +transceiver or a switch (that needs to be managed by using +fixed-link). + +Signed-off-by: Giuseppe Cavallaro +Reviewed-by: Andreas Färber +Tested-by: Frank Schäfer +Cc: Gabriel Fernandez +Cc: Dinh Nguyen +Cc: David S. Miller +Cc: Phil Reid +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 11 +-- + drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 19 +---- + .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 84 +++++++++++++++++----- + include/linux/stmmac.h | 2 +- + 4 files changed, 71 insertions(+), 45 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index c21015b..389d7d0 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -271,7 +271,6 @@ static void stmmac_eee_ctrl_timer(unsigned long arg) + */ + bool stmmac_eee_init(struct stmmac_priv *priv) + { +- char *phy_bus_name = priv->plat->phy_bus_name; + unsigned long flags; + bool ret = false; + +@@ -283,7 +282,7 @@ bool stmmac_eee_init(struct stmmac_priv *priv) + goto out; + + /* Never init EEE in case of a switch is attached */ +- if (phy_bus_name && (!strcmp(phy_bus_name, "fixed"))) ++ if (priv->phydev->is_pseudo_fixed_link) + goto out; + + /* MAC core supports the EEE feature. */ +@@ -820,12 +819,8 @@ static int stmmac_init_phy(struct net_device *dev) + phydev = of_phy_connect(dev, priv->plat->phy_node, + &stmmac_adjust_link, 0, interface); + } else { +- if (priv->plat->phy_bus_name) +- snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", +- priv->plat->phy_bus_name, priv->plat->bus_id); +- else +- snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", +- priv->plat->bus_id); ++ snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", ++ priv->plat->bus_id); + + snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, + priv->plat->phy_addr); +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +index 0faf163..3f5512f 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +@@ -198,29 +198,12 @@ int stmmac_mdio_register(struct net_device *ndev) + struct mii_bus *new_bus; + struct stmmac_priv *priv = netdev_priv(ndev); + struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data; ++ struct device_node *mdio_node = priv->plat->mdio_node; + int addr, found; +- struct device_node *mdio_node = NULL; +- struct device_node *child_node = NULL; + + if (!mdio_bus_data) + return 0; + +- if (IS_ENABLED(CONFIG_OF)) { +- for_each_child_of_node(priv->device->of_node, child_node) { +- if (of_device_is_compatible(child_node, +- "snps,dwmac-mdio")) { +- mdio_node = child_node; +- break; +- } +- } +- +- if (mdio_node) { +- netdev_dbg(ndev, "FOUND MDIO subnode\n"); +- } else { +- netdev_warn(ndev, "No MDIO subnode found\n"); +- } +- } +- + new_bus = mdiobus_alloc(); + if (new_bus == NULL) + return -ENOMEM; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +index 6a52fa1..190fb6d 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -96,6 +96,69 @@ static int dwmac1000_validate_ucast_entries(int ucast_entries) + } + + /** ++ * stmmac_dt_phy - parse device-tree driver parameters to allocate PHY resources ++ * @plat: driver data platform structure ++ * @np: device tree node ++ * @dev: device pointer ++ * Description: ++ * The mdio bus will be allocated in case of a phy transceiver is on board; ++ * it will be NULL if the fixed-link is configured. ++ * If there is the "snps,dwmac-mdio" sub-node the mdio will be allocated ++ * in any case (for DSA, mdio must be registered even if fixed-link). ++ * The table below sums the supported configurations: ++ * ------------------------------- ++ * snps,phy-addr | Y ++ * ------------------------------- ++ * phy-handle | Y ++ * ------------------------------- ++ * fixed-link | N ++ * ------------------------------- ++ * snps,dwmac-mdio | ++ * even if | Y ++ * fixed-link | ++ * ------------------------------- ++ * ++ * It returns 0 in case of success otherwise -ENODEV. ++ */ ++static int stmmac_dt_phy(struct plat_stmmacenet_data *plat, ++ struct device_node *np, struct device *dev) ++{ ++ bool mdio = true; ++ ++ /* If phy-handle property is passed from DT, use it as the PHY */ ++ plat->phy_node = of_parse_phandle(np, "phy-handle", 0); ++ if (plat->phy_node) ++ dev_dbg(dev, "Found phy-handle subnode\n"); ++ ++ /* If phy-handle is not specified, check if we have a fixed-phy */ ++ if (!plat->phy_node && of_phy_is_fixed_link(np)) { ++ if ((of_phy_register_fixed_link(np) < 0)) ++ return -ENODEV; ++ ++ dev_dbg(dev, "Found fixed-link subnode\n"); ++ plat->phy_node = of_node_get(np); ++ mdio = false; ++ } ++ ++ /* If snps,dwmac-mdio is passed from DT, always register the MDIO */ ++ for_each_child_of_node(np, plat->mdio_node) { ++ if (of_device_is_compatible(plat->mdio_node, "snps,dwmac-mdio")) ++ break; ++ } ++ ++ if (plat->mdio_node) { ++ dev_dbg(dev, "Found MDIO subnode\n"); ++ mdio = true; ++ } ++ ++ if (mdio) ++ plat->mdio_bus_data = ++ devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data), ++ GFP_KERNEL); ++ return 0; ++} ++ ++/** + * stmmac_probe_config_dt - parse device-tree driver parameters + * @pdev: platform_device structure + * @plat: driver data platform structure +@@ -129,30 +192,15 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) + /* Default to phy auto-detection */ + plat->phy_addr = -1; + +- /* If we find a phy-handle property, use it as the PHY */ +- plat->phy_node = of_parse_phandle(np, "phy-handle", 0); +- +- /* If phy-handle is not specified, check if we have a fixed-phy */ +- if (!plat->phy_node && of_phy_is_fixed_link(np)) { +- if ((of_phy_register_fixed_link(np) < 0)) +- return ERR_PTR(-ENODEV); +- +- plat->phy_node = of_node_get(np); +- } +- + /* "snps,phy-addr" is not a standard property. Mark it as deprecated + * and warn of its use. Remove this when phy node support is added. + */ + if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0) + dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n"); + +- if ((plat->phy_node && !of_phy_is_fixed_link(np)) || plat->phy_bus_name) +- plat->mdio_bus_data = NULL; +- else +- plat->mdio_bus_data = +- devm_kzalloc(&pdev->dev, +- sizeof(struct stmmac_mdio_bus_data), +- GFP_KERNEL); ++ /* To Configure PHY by using all device-tree supported properties */ ++ if (stmmac_dt_phy(plat, np, &pdev->dev)) ++ return ERR_PTR(-ENODEV); + + of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size); + +diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h +index eead8ab..8b1ff2b 100644 +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -94,12 +94,12 @@ struct stmmac_dma_cfg { + }; + + struct plat_stmmacenet_data { +- char *phy_bus_name; + int bus_id; + int phy_addr; + int interface; + struct stmmac_mdio_bus_data *mdio_bus_data; + struct device_node *phy_node; ++ struct device_node *mdio_node; + struct stmmac_dma_cfg *dma_cfg; + int clk_csr; + int has_gmac; +-- +2.5.0 + diff --git a/tip-x86-boot-x86-KASLR-x86-power-Remove-x86-hibernation-restrictions.patch b/tip-x86-boot-x86-KASLR-x86-power-Remove-x86-hibernation-restrictions.patch new file mode 100644 index 000000000..7d79fbcef --- /dev/null +++ b/tip-x86-boot-x86-KASLR-x86-power-Remove-x86-hibernation-restrictions.patch @@ -0,0 +1,131 @@ +From patchwork Sun Jun 26 11:01:18 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [tip:x86/boot] x86/KASLR, + x86/power: Remove x86 hibernation restrictions +From: tip-bot for Jacob Shin +X-Patchwork-Id: 9199367 +Message-Id: +To: linux-tip-commits@vger.kernel.org +Cc: pavel@ucw.cz, linux-pm@vger.kernel.org, yinghai@kernel.org, + mingo@kernel.org, linux-kernel@vger.kernel.org, tglx@linutronix.de, + corbet@lwn.net, dvlasenk@redhat.com, sds@tycho.nsa.gov, + peterz@infradead.org, logang@deltatee.com, bhe@redhat.com, + torvalds@linux-foundation.org, bp@alien8.de, luto@kernel.org, + keescook@chromium.org, hpa@zytor.com, brgerst@gmail.com, + len.brown@intel.com +Date: Sun, 26 Jun 2016 04:01:18 -0700 + +Commit-ID: 65fe935dd2387a4faf15314c73f5e6d31ef0217e +Gitweb: http://git.kernel.org/tip/65fe935dd2387a4faf15314c73f5e6d31ef0217e +Author: Kees Cook +AuthorDate: Mon, 13 Jun 2016 15:10:02 -0700 +Committer: Ingo Molnar +CommitDate: Sun, 26 Jun 2016 12:32:03 +0200 + +x86/KASLR, x86/power: Remove x86 hibernation restrictions + +With the following fix: + + 70595b479ce1 ("x86/power/64: Fix crash whan the hibernation code passes control to the image kernel") + +... there is no longer a problem with hibernation resuming a +KASLR-booted kernel image, so remove the restriction. + +Signed-off-by: Kees Cook +Cc: Andy Lutomirski +Cc: Baoquan He +Cc: Borislav Petkov +Cc: Brian Gerst +Cc: Denys Vlasenko +Cc: H. Peter Anvin +Cc: Jonathan Corbet +Cc: Len Brown +Cc: Linus Torvalds +Cc: Linux PM list +Cc: Logan Gunthorpe +Cc: Pavel Machek +Cc: Peter Zijlstra +Cc: Stephen Smalley +Cc: Thomas Gleixner +Cc: Yinghai Lu +Cc: linux-doc@vger.kernel.org +Link: http://lkml.kernel.org/r/20160613221002.GA29719@www.outflux.net +Signed-off-by: Ingo Molnar +--- + Documentation/kernel-parameters.txt | 10 ++++------ + arch/x86/boot/compressed/kaslr.c | 7 ------- + kernel/power/hibernate.c | 6 ------ + 3 files changed, 4 insertions(+), 19 deletions(-) + +-- +To unsubscribe from this list: send the line "unsubscribe linux-pm" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 82b42c9..fa8c6d4 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -1803,12 +1803,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + js= [HW,JOY] Analog joystick + See Documentation/input/joystick.txt. + +- kaslr/nokaslr [X86] +- Enable/disable kernel and module base offset ASLR +- (Address Space Layout Randomization) if built into +- the kernel. When CONFIG_HIBERNATION is selected, +- kASLR is disabled by default. When kASLR is enabled, +- hibernation will be disabled. ++ nokaslr [KNL] ++ When CONFIG_RANDOMIZE_BASE is set, this disables ++ kernel and module base offset ASLR (Address Space ++ Layout Randomization). + + keepinitrd [HW,ARM] + +diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c +index cfeb025..dff4217 100644 +--- a/arch/x86/boot/compressed/kaslr.c ++++ b/arch/x86/boot/compressed/kaslr.c +@@ -471,17 +471,10 @@ unsigned char *choose_random_location(unsigned long input, + unsigned long choice = output; + unsigned long random_addr; + +-#ifdef CONFIG_HIBERNATION +- if (!cmdline_find_option_bool("kaslr")) { +- warn("KASLR disabled: 'kaslr' not on cmdline (hibernation selected)."); +- goto out; +- } +-#else + if (cmdline_find_option_bool("nokaslr")) { + warn("KASLR disabled: 'nokaslr' on cmdline."); + goto out; + } +-#endif + + boot_params->hdr.loadflags |= KASLR_FLAG; + +diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c +index fca9254..9021387 100644 +--- a/kernel/power/hibernate.c ++++ b/kernel/power/hibernate.c +@@ -1154,11 +1154,6 @@ static int __init nohibernate_setup(char *str) + return 1; + } + +-static int __init kaslr_nohibernate_setup(char *str) +-{ +- return nohibernate_setup(str); +-} +- + static int __init page_poison_nohibernate_setup(char *str) + { + #ifdef CONFIG_PAGE_POISONING_ZERO +@@ -1182,5 +1177,4 @@ __setup("hibernate=", hibernate_setup); + __setup("resumewait", resumewait_setup); + __setup("resumedelay=", resumedelay_setup); + __setup("nohibernate", nohibernate_setup); +-__setup("kaslr", kaslr_nohibernate_setup); + __setup("page_poison=", page_poison_nohibernate_setup); diff --git a/tipc-check-nl-sock-before-parsing-nested-attributes.patch b/tipc-check-nl-sock-before-parsing-nested-attributes.patch new file mode 100644 index 000000000..09bfe1485 --- /dev/null +++ b/tipc-check-nl-sock-before-parsing-nested-attributes.patch @@ -0,0 +1,36 @@ +From 45e093ae2830cd1264677d47ff9a95a71f5d9f9c Mon Sep 17 00:00:00 2001 +From: Richard Alpe +Date: Mon, 16 May 2016 11:14:54 +0200 +Subject: [PATCH] tipc: check nl sock before parsing nested attributes + +Make sure the socket for which the user is listing publication exists +before parsing the socket netlink attributes. + +Prior to this patch a call without any socket caused a NULL pointer +dereference in tipc_nl_publ_dump(). + +Tested-and-reported-by: Baozeng Ding +Signed-off-by: Richard Alpe +Acked-by: Jon Maloy +Signed-off-by: David S. Miller +--- + net/tipc/socket.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/tipc/socket.c b/net/tipc/socket.c +index 12628890c219..3b7a79991d55 100644 +--- a/net/tipc/socket.c ++++ b/net/tipc/socket.c +@@ -2853,6 +2853,9 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb) + if (err) + return err; + ++ if (!attrs[TIPC_NLA_SOCK]) ++ return -EINVAL; ++ + err = nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, + attrs[TIPC_NLA_SOCK], + tipc_nl_sock_policy); +-- +2.5.5 + diff --git a/v3-vfio-pci-Fix-integer-overflows-bitmask-check.patch b/v3-vfio-pci-Fix-integer-overflows-bitmask-check.patch new file mode 100644 index 000000000..5278d4486 --- /dev/null +++ b/v3-vfio-pci-Fix-integer-overflows-bitmask-check.patch @@ -0,0 +1,102 @@ +From patchwork Wed Oct 12 16:51:24 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3] vfio/pci: Fix integer overflows, bitmask check +From: Vlad Tsyrklevich +X-Patchwork-Id: 9373631 +Message-Id: <1476291084-50737-1-git-send-email-vlad@tsyrklevich.net> +To: kvm@vger.kernel.org +Cc: alex.williamson@redhat.com, Vlad Tsyrklevich +Date: Wed, 12 Oct 2016 18:51:24 +0200 + +The VFIO_DEVICE_SET_IRQS ioctl did not sufficiently sanitize +user-supplied integers, potentially allowing memory corruption. This +patch adds appropriate integer overflow checks, checks the range bounds +for VFIO_IRQ_SET_DATA_NONE, and also verifies that only single element +in the VFIO_IRQ_SET_DATA_TYPE_MASK bitmask is set. +VFIO_IRQ_SET_ACTION_TYPE_MASK is already correctly checked later in +vfio_pci_set_irqs_ioctl(). + +Furthermore, a kzalloc is changed to a kcalloc because the use of a +kzalloc with an integer multiplication allowed an integer overflow +condition to be reached without this patch. kcalloc checks for overflow +and should prevent a similar occurrence. + +Signed-off-by: Vlad Tsyrklevich +--- + drivers/vfio/pci/vfio_pci.c | 33 +++++++++++++++++++++------------ + drivers/vfio/pci/vfio_pci_intrs.c | 2 +- + 2 files changed, 22 insertions(+), 13 deletions(-) + +diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c +index d624a52..031bc08 100644 +--- a/drivers/vfio/pci/vfio_pci.c ++++ b/drivers/vfio/pci/vfio_pci.c +@@ -829,8 +829,9 @@ static long vfio_pci_ioctl(void *device_data, + + } else if (cmd == VFIO_DEVICE_SET_IRQS) { + struct vfio_irq_set hdr; ++ size_t size; + u8 *data = NULL; +- int ret = 0; ++ int max, ret = 0; + + minsz = offsetofend(struct vfio_irq_set, count); + +@@ -838,23 +839,31 @@ static long vfio_pci_ioctl(void *device_data, + return -EFAULT; + + if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS || ++ hdr.count >= (U32_MAX - hdr.start) || + hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | + VFIO_IRQ_SET_ACTION_TYPE_MASK)) + return -EINVAL; + +- if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { +- size_t size; +- int max = vfio_pci_get_irq_count(vdev, hdr.index); ++ max = vfio_pci_get_irq_count(vdev, hdr.index); ++ if (hdr.start >= max || hdr.start + hdr.count > max) ++ return -EINVAL; + +- if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) +- size = sizeof(uint8_t); +- else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) +- size = sizeof(int32_t); +- else +- return -EINVAL; ++ switch (hdr.flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { ++ case VFIO_IRQ_SET_DATA_NONE: ++ size = 0; ++ break; ++ case VFIO_IRQ_SET_DATA_BOOL: ++ size = sizeof(uint8_t); ++ break; ++ case VFIO_IRQ_SET_DATA_EVENTFD: ++ size = sizeof(int32_t); ++ break; ++ default: ++ return -EINVAL; ++ } + +- if (hdr.argsz - minsz < hdr.count * size || +- hdr.start >= max || hdr.start + hdr.count > max) ++ if (size) { ++ if (hdr.argsz - minsz < hdr.count * size) + return -EINVAL; + + data = memdup_user((void __user *)(arg + minsz), +diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c +index c2e6089..1c46045 100644 +--- a/drivers/vfio/pci/vfio_pci_intrs.c ++++ b/drivers/vfio/pci/vfio_pci_intrs.c +@@ -256,7 +256,7 @@ static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) + if (!is_irq_none(vdev)) + return -EINVAL; + +- vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); ++ vdev->ctx = kcalloc(nvec, sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); + if (!vdev->ctx) + return -ENOMEM; + diff --git a/vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch b/vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch new file mode 100644 index 000000000..f9e7e6e61 --- /dev/null +++ b/vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch @@ -0,0 +1,110 @@ +From 14588dfe2e411056df5ba85ef88ad51730a2fa0a Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Sat, 15 Aug 2015 20:27:13 -0500 +Subject: [PATCH 2/2] vfs: Test for and handle paths that are unreachable from + their mnt_root + +commit 397d425dc26da728396e66d392d5dcb8dac30c37 upstream. + +In rare cases a directory can be renamed out from under a bind mount. +In those cases without special handling it becomes possible to walk up +the directory tree to the root dentry of the filesystem and down +from the root dentry to every other file or directory on the filesystem. + +Like division by zero .. from an unconnected path can not be given +a useful semantic as there is no predicting at which path component +the code will realize it is unconnected. We certainly can not match +the current behavior as the current behavior is a security hole. + +Therefore when encounting .. when following an unconnected path +return -ENOENT. + +- Add a function path_connected to verify path->dentry is reachable + from path->mnt.mnt_root. AKA to validate that rename did not do + something nasty to the bind mount. + + To avoid races path_connected must be called after following a path + component to it's next path component. + +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Al Viro +--- + fs/namei.c | 27 +++++++++++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/fs/namei.c b/fs/namei.c +index 1c2105ed20c5..29b927938b8c 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -560,6 +560,24 @@ static int __nd_alloc_stack(struct nameidata *nd) + return 0; + } + ++/** ++ * path_connected - Verify that a path->dentry is below path->mnt.mnt_root ++ * @path: nameidate to verify ++ * ++ * Rename can sometimes move a file or directory outside of a bind ++ * mount, path_connected allows those cases to be detected. ++ */ ++static bool path_connected(const struct path *path) ++{ ++ struct vfsmount *mnt = path->mnt; ++ ++ /* Only bind mounts can have disconnected paths */ ++ if (mnt->mnt_root == mnt->mnt_sb->s_root) ++ return true; ++ ++ return is_subdir(path->dentry, mnt->mnt_root); ++} ++ + static inline int nd_alloc_stack(struct nameidata *nd) + { + if (likely(nd->depth != EMBEDDED_LEVELS)) +@@ -1296,6 +1314,8 @@ static int follow_dotdot_rcu(struct nameidata *nd) + return -ECHILD; + nd->path.dentry = parent; + nd->seq = seq; ++ if (unlikely(!path_connected(&nd->path))) ++ return -ENOENT; + break; + } else { + struct mount *mnt = real_mount(nd->path.mnt); +@@ -1396,7 +1416,7 @@ static void follow_mount(struct path *path) + } + } + +-static void follow_dotdot(struct nameidata *nd) ++static int follow_dotdot(struct nameidata *nd) + { + if (!nd->root.mnt) + set_root(nd); +@@ -1412,6 +1432,8 @@ static void follow_dotdot(struct nameidata *nd) + /* rare case of legitimate dget_parent()... */ + nd->path.dentry = dget_parent(nd->path.dentry); + dput(old); ++ if (unlikely(!path_connected(&nd->path))) ++ return -ENOENT; + break; + } + if (!follow_up(&nd->path)) +@@ -1419,6 +1441,7 @@ static void follow_dotdot(struct nameidata *nd) + } + follow_mount(&nd->path); + nd->inode = nd->path.dentry->d_inode; ++ return 0; + } + + /* +@@ -1634,7 +1657,7 @@ static inline int handle_dots(struct nameidata *nd, int type) + if (nd->flags & LOOKUP_RCU) { + return follow_dotdot_rcu(nd); + } else +- follow_dotdot(nd); ++ return follow_dotdot(nd); + } + return 0; + } +-- +2.4.3 + diff --git a/vmwgfx-Rework-device-initialization.patch b/vmwgfx-Rework-device-initialization.patch new file mode 100644 index 000000000..183ba9c28 --- /dev/null +++ b/vmwgfx-Rework-device-initialization.patch @@ -0,0 +1,890 @@ +From c1d9b32d8ee2e97e2867fa759eb84d436cca0311 Mon Sep 17 00:00:00 2001 +From: Thomas Hellstrom +Date: Thu, 25 Jun 2015 10:47:43 -0700 +Subject: [PATCH 1/2] vmwgfx: Rework device initialization + +This commit reworks device initialization so that we always enable the +FIFO at driver load, deferring SVGA enable until either first modeset +or fbdev enable. +This should always leave the fifo properly enabled for render- and +control nodes. +In addition, +*) We disable the use of VRAM when SVGA is not enabled. +*) We simplify PM support so that we only throw out resources on hibernate, +not on suspend, since the device keeps its state on suspend. + +Signed-off-by: Thomas Hellstrom +Reviewed-by: Sinclair Yeh +--- + drivers/gpu/drm/vmwgfx/vmwgfx_context.c | 8 +- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 337 ++++++++++++++++++-------------- + drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 19 +- + drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 4 + + drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c | 12 +- + drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 + + drivers/gpu/drm/vmwgfx/vmwgfx_mob.c | 6 +- + drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 + + drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 4 +- + drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 12 +- + 10 files changed, 230 insertions(+), 174 deletions(-) + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c +index 5ac92874404d..a8e370a55e90 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c +@@ -140,7 +140,7 @@ static void vmw_hw_context_destroy(struct vmw_resource *res) + cmd->body.cid = cpu_to_le32(res->id); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + } + + static int vmw_gb_context_init(struct vmw_private *dev_priv, +@@ -220,7 +220,7 @@ static int vmw_context_init(struct vmw_private *dev_priv, + cmd->body.cid = cpu_to_le32(res->id); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + vmw_resource_activate(res, vmw_hw_context_destroy); + return 0; + +@@ -281,7 +281,7 @@ static int vmw_gb_context_create(struct vmw_resource *res) + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = res->id; + vmw_fifo_commit(dev_priv, sizeof(*cmd)); +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + + return 0; + +@@ -414,7 +414,7 @@ static int vmw_gb_context_destroy(struct vmw_resource *res) + if (dev_priv->query_cid == res->id) + dev_priv->query_cid_valid = false; + vmw_resource_release_id(res); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + + return 0; + } +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +index 620bb5cf617c..a4766acd0ea2 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +@@ -339,24 +339,47 @@ static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv) + return ret; + } + +-static int vmw_request_device(struct vmw_private *dev_priv) ++/** ++ * vmw_request_device_late - Perform late device setup ++ * ++ * @dev_priv: Pointer to device private. ++ * ++ * This function performs setup of otables and enables large command ++ * buffer submission. These tasks are split out to a separate function ++ * because it reverts vmw_release_device_early and is intended to be used ++ * by an error path in the hibernation code. ++ */ ++static int vmw_request_device_late(struct vmw_private *dev_priv) + { + int ret; + +- ret = vmw_fifo_init(dev_priv, &dev_priv->fifo); +- if (unlikely(ret != 0)) { +- DRM_ERROR("Unable to initialize FIFO.\n"); +- return ret; +- } +- vmw_fence_fifo_up(dev_priv->fman); + if (dev_priv->has_mob) { + ret = vmw_otables_setup(dev_priv); + if (unlikely(ret != 0)) { + DRM_ERROR("Unable to initialize " + "guest Memory OBjects.\n"); +- goto out_no_mob; ++ return ret; + } + } ++ ++ return 0; ++} ++ ++static int vmw_request_device(struct vmw_private *dev_priv) ++{ ++ int ret; ++ ++ ret = vmw_fifo_init(dev_priv, &dev_priv->fifo); ++ if (unlikely(ret != 0)) { ++ DRM_ERROR("Unable to initialize FIFO.\n"); ++ return ret; ++ } ++ vmw_fence_fifo_up(dev_priv->fman); ++ ++ ret = vmw_request_device_late(dev_priv); ++ if (ret) ++ goto out_no_mob; ++ + ret = vmw_dummy_query_bo_create(dev_priv); + if (unlikely(ret != 0)) + goto out_no_query_bo; +@@ -364,15 +387,25 @@ static int vmw_request_device(struct vmw_private *dev_priv) + return 0; + + out_no_query_bo: +- if (dev_priv->has_mob) ++ if (dev_priv->has_mob) { ++ (void) ttm_bo_evict_mm(&dev_priv->bdev, VMW_PL_MOB); + vmw_otables_takedown(dev_priv); ++ } + out_no_mob: + vmw_fence_fifo_down(dev_priv->fman); + vmw_fifo_release(dev_priv, &dev_priv->fifo); + return ret; + } + +-static void vmw_release_device(struct vmw_private *dev_priv) ++/** ++ * vmw_release_device_early - Early part of fifo takedown. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * ++ * This is the first part of command submission takedown, to be called before ++ * buffer management is taken down. ++ */ ++static void vmw_release_device_early(struct vmw_private *dev_priv) + { + /* + * Previous destructions should've released +@@ -382,64 +415,24 @@ static void vmw_release_device(struct vmw_private *dev_priv) + BUG_ON(dev_priv->pinned_bo != NULL); + + ttm_bo_unref(&dev_priv->dummy_query_bo); +- if (dev_priv->has_mob) ++ if (dev_priv->has_mob) { ++ ttm_bo_evict_mm(&dev_priv->bdev, VMW_PL_MOB); + vmw_otables_takedown(dev_priv); +- vmw_fence_fifo_down(dev_priv->fman); +- vmw_fifo_release(dev_priv, &dev_priv->fifo); +-} +- +- +-/** +- * Increase the 3d resource refcount. +- * If the count was prevously zero, initialize the fifo, switching to svga +- * mode. Note that the master holds a ref as well, and may request an +- * explicit switch to svga mode if fb is not running, using @unhide_svga. +- */ +-int vmw_3d_resource_inc(struct vmw_private *dev_priv, +- bool unhide_svga) +-{ +- int ret = 0; +- +- mutex_lock(&dev_priv->release_mutex); +- if (unlikely(dev_priv->num_3d_resources++ == 0)) { +- ret = vmw_request_device(dev_priv); +- if (unlikely(ret != 0)) +- --dev_priv->num_3d_resources; +- } else if (unhide_svga) { +- vmw_write(dev_priv, SVGA_REG_ENABLE, +- vmw_read(dev_priv, SVGA_REG_ENABLE) & +- ~SVGA_REG_ENABLE_HIDE); + } +- +- mutex_unlock(&dev_priv->release_mutex); +- return ret; + } + + /** +- * Decrease the 3d resource refcount. +- * If the count reaches zero, disable the fifo, switching to vga mode. +- * Note that the master holds a refcount as well, and may request an +- * explicit switch to vga mode when it releases its refcount to account +- * for the situation of an X server vt switch to VGA with 3d resources +- * active. ++ * vmw_release_device_late - Late part of fifo takedown. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * ++ * This is the last part of the command submission takedown, to be called when ++ * command submission is no longer needed. It may wait on pending fences. + */ +-void vmw_3d_resource_dec(struct vmw_private *dev_priv, +- bool hide_svga) ++static void vmw_release_device_late(struct vmw_private *dev_priv) + { +- int32_t n3d; +- +- mutex_lock(&dev_priv->release_mutex); +- if (unlikely(--dev_priv->num_3d_resources == 0)) +- vmw_release_device(dev_priv); +- else if (hide_svga) +- vmw_write(dev_priv, SVGA_REG_ENABLE, +- vmw_read(dev_priv, SVGA_REG_ENABLE) | +- SVGA_REG_ENABLE_HIDE); +- +- n3d = (int32_t) dev_priv->num_3d_resources; +- mutex_unlock(&dev_priv->release_mutex); +- +- BUG_ON(n3d < 0); ++ vmw_fence_fifo_down(dev_priv->fman); ++ vmw_fifo_release(dev_priv, &dev_priv->fifo); + } + + /** +@@ -603,6 +596,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) + spin_lock_init(&dev_priv->hw_lock); + spin_lock_init(&dev_priv->waiter_lock); + spin_lock_init(&dev_priv->cap_lock); ++ spin_lock_init(&dev_priv->svga_lock); + + for (i = vmw_res_context; i < vmw_res_max; ++i) { + idr_init(&dev_priv->res_idr[i]); +@@ -714,17 +708,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) + dev_priv->active_master = &dev_priv->fbdev_master; + + +- ret = ttm_bo_device_init(&dev_priv->bdev, +- dev_priv->bo_global_ref.ref.object, +- &vmw_bo_driver, +- dev->anon_inode->i_mapping, +- VMWGFX_FILE_PAGE_OFFSET, +- false); +- if (unlikely(ret != 0)) { +- DRM_ERROR("Failed initializing TTM buffer object driver.\n"); +- goto out_err1; +- } +- + dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start, + dev_priv->mmio_size); + +@@ -787,13 +770,28 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) + goto out_no_fman; + } + ++ ret = ttm_bo_device_init(&dev_priv->bdev, ++ dev_priv->bo_global_ref.ref.object, ++ &vmw_bo_driver, ++ dev->anon_inode->i_mapping, ++ VMWGFX_FILE_PAGE_OFFSET, ++ false); ++ if (unlikely(ret != 0)) { ++ DRM_ERROR("Failed initializing TTM buffer object driver.\n"); ++ goto out_no_bdev; ++ } + ++ /* ++ * Enable VRAM, but initially don't use it until SVGA is enabled and ++ * unhidden. ++ */ + ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM, + (dev_priv->vram_size >> PAGE_SHIFT)); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed initializing memory manager for VRAM.\n"); + goto out_no_vram; + } ++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false; + + dev_priv->has_gmr = true; + if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || +@@ -814,18 +812,18 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) + } + } + +- vmw_kms_save_vga(dev_priv); +- +- /* Start kms and overlay systems, needs fifo. */ + ret = vmw_kms_init(dev_priv); + if (unlikely(ret != 0)) + goto out_no_kms; + vmw_overlay_init(dev_priv); + ++ ret = vmw_request_device(dev_priv); ++ if (ret) ++ goto out_no_fifo; ++ + if (dev_priv->enable_fb) { +- ret = vmw_3d_resource_inc(dev_priv, true); +- if (unlikely(ret != 0)) +- goto out_no_fifo; ++ vmw_fifo_resource_inc(dev_priv); ++ vmw_svga_enable(dev_priv); + vmw_fb_init(dev_priv); + } + +@@ -838,13 +836,14 @@ out_no_fifo: + vmw_overlay_close(dev_priv); + vmw_kms_close(dev_priv); + out_no_kms: +- vmw_kms_restore_vga(dev_priv); + if (dev_priv->has_mob) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); + if (dev_priv->has_gmr) + (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + out_no_vram: ++ (void)ttm_bo_device_release(&dev_priv->bdev); ++out_no_bdev: + vmw_fence_manager_takedown(dev_priv->fman); + out_no_fman: + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) +@@ -860,8 +859,6 @@ out_err4: + iounmap(dev_priv->mmio_virt); + out_err3: + arch_phys_wc_del(dev_priv->mmio_mtrr); +- (void)ttm_bo_device_release(&dev_priv->bdev); +-out_err1: + vmw_ttm_global_release(dev_priv); + out_err0: + for (i = vmw_res_context; i < vmw_res_max; ++i) +@@ -883,18 +880,22 @@ static int vmw_driver_unload(struct drm_device *dev) + vfree(dev_priv->ctx.cmd_bounce); + if (dev_priv->enable_fb) { + vmw_fb_close(dev_priv); +- vmw_kms_restore_vga(dev_priv); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); ++ vmw_svga_disable(dev_priv); + } ++ + vmw_kms_close(dev_priv); + vmw_overlay_close(dev_priv); + +- if (dev_priv->has_mob) +- (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); + if (dev_priv->has_gmr) + (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR); + (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); + ++ vmw_release_device_early(dev_priv); ++ if (dev_priv->has_mob) ++ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB); ++ (void) ttm_bo_device_release(&dev_priv->bdev); ++ vmw_release_device_late(dev_priv); + vmw_fence_manager_takedown(dev_priv->fman); + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) + drm_irq_uninstall(dev_priv->dev); +@@ -1148,27 +1149,13 @@ static int vmw_master_set(struct drm_device *dev, + struct vmw_master *vmaster = vmw_master(file_priv->master); + int ret = 0; + +- if (!dev_priv->enable_fb) { +- ret = vmw_3d_resource_inc(dev_priv, true); +- if (unlikely(ret != 0)) +- return ret; +- vmw_kms_save_vga(dev_priv); +- vmw_write(dev_priv, SVGA_REG_TRACES, 0); +- } +- + if (active) { + BUG_ON(active != &dev_priv->fbdev_master); + ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile); + if (unlikely(ret != 0)) +- goto out_no_active_lock; ++ return ret; + + ttm_lock_set_kill(&active->lock, true, SIGTERM); +- ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM); +- if (unlikely(ret != 0)) { +- DRM_ERROR("Unable to clean VRAM on " +- "master drop.\n"); +- } +- + dev_priv->active_master = NULL; + } + +@@ -1182,14 +1169,6 @@ static int vmw_master_set(struct drm_device *dev, + dev_priv->active_master = vmaster; + + return 0; +- +-out_no_active_lock: +- if (!dev_priv->enable_fb) { +- vmw_kms_restore_vga(dev_priv); +- vmw_3d_resource_dec(dev_priv, true); +- vmw_write(dev_priv, SVGA_REG_TRACES, 1); +- } +- return ret; + } + + static void vmw_master_drop(struct drm_device *dev, +@@ -1214,16 +1193,9 @@ static void vmw_master_drop(struct drm_device *dev, + } + + ttm_lock_set_kill(&vmaster->lock, false, SIGTERM); +- vmw_execbuf_release_pinned_bo(dev_priv); + +- if (!dev_priv->enable_fb) { +- ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM); +- if (unlikely(ret != 0)) +- DRM_ERROR("Unable to clean VRAM on master drop.\n"); +- vmw_kms_restore_vga(dev_priv); +- vmw_3d_resource_dec(dev_priv, true); +- vmw_write(dev_priv, SVGA_REG_TRACES, 1); +- } ++ if (!dev_priv->enable_fb) ++ vmw_svga_disable(dev_priv); + + dev_priv->active_master = &dev_priv->fbdev_master; + ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM); +@@ -1233,6 +1205,74 @@ static void vmw_master_drop(struct drm_device *dev, + vmw_fb_on(dev_priv); + } + ++/** ++ * __vmw_svga_enable - Enable SVGA mode, FIFO and use of VRAM. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * Needs the reservation sem to be held in non-exclusive mode. ++ */ ++void __vmw_svga_enable(struct vmw_private *dev_priv) ++{ ++ spin_lock(&dev_priv->svga_lock); ++ if (!dev_priv->bdev.man[TTM_PL_VRAM].use_type) { ++ vmw_write(dev_priv, SVGA_REG_ENABLE, SVGA_REG_ENABLE); ++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = true; ++ } ++ spin_unlock(&dev_priv->svga_lock); ++} ++ ++/** ++ * vmw_svga_enable - Enable SVGA mode, FIFO and use of VRAM. ++ * ++ * @dev_priv: Pointer to device private struct. ++ */ ++void vmw_svga_enable(struct vmw_private *dev_priv) ++{ ++ ttm_read_lock(&dev_priv->reservation_sem, false); ++ __vmw_svga_enable(dev_priv); ++ ttm_read_unlock(&dev_priv->reservation_sem); ++} ++ ++/** ++ * __vmw_svga_disable - Disable SVGA mode and use of VRAM. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * Needs the reservation sem to be held in exclusive mode. ++ * Will not empty VRAM. VRAM must be emptied by caller. ++ */ ++void __vmw_svga_disable(struct vmw_private *dev_priv) ++{ ++ spin_lock(&dev_priv->svga_lock); ++ if (dev_priv->bdev.man[TTM_PL_VRAM].use_type) { ++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false; ++ vmw_write(dev_priv, SVGA_REG_ENABLE, ++ SVGA_REG_ENABLE_ENABLE_HIDE); ++ } ++ spin_unlock(&dev_priv->svga_lock); ++} ++ ++/** ++ * vmw_svga_disable - Disable SVGA_MODE, and use of VRAM. Keep the fifo ++ * running. ++ * ++ * @dev_priv: Pointer to device private struct. ++ * Will empty VRAM. ++ */ ++void vmw_svga_disable(struct vmw_private *dev_priv) ++{ ++ ttm_write_lock(&dev_priv->reservation_sem, false); ++ spin_lock(&dev_priv->svga_lock); ++ if (dev_priv->bdev.man[TTM_PL_VRAM].use_type) { ++ dev_priv->bdev.man[TTM_PL_VRAM].use_type = false; ++ vmw_write(dev_priv, SVGA_REG_ENABLE, ++ SVGA_REG_ENABLE_ENABLE_HIDE); ++ spin_unlock(&dev_priv->svga_lock); ++ if (ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM)) ++ DRM_ERROR("Failed evicting VRAM buffers.\n"); ++ } else ++ spin_unlock(&dev_priv->svga_lock); ++ ttm_write_unlock(&dev_priv->reservation_sem); ++} + + static void vmw_remove(struct pci_dev *pdev) + { +@@ -1250,21 +1290,21 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, + + switch (val) { + case PM_HIBERNATION_PREPARE: +- case PM_SUSPEND_PREPARE: + ttm_suspend_lock(&dev_priv->reservation_sem); + +- /** ++ /* + * This empties VRAM and unbinds all GMR bindings. + * Buffer contents is moved to swappable memory. + */ + vmw_execbuf_release_pinned_bo(dev_priv); + vmw_resource_evict_all(dev_priv); ++ vmw_release_device_early(dev_priv); + ttm_bo_swapout_all(&dev_priv->bdev); +- ++ vmw_fence_fifo_down(dev_priv->fman); + break; + case PM_POST_HIBERNATION: +- case PM_POST_SUSPEND: + case PM_POST_RESTORE: ++ vmw_fence_fifo_up(dev_priv->fman); + ttm_suspend_unlock(&dev_priv->reservation_sem); + + break; +@@ -1276,20 +1316,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, + return 0; + } + +-/** +- * These might not be needed with the virtual SVGA device. +- */ +- + static int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) + { + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + +- if (dev_priv->num_3d_resources != 0) { +- DRM_INFO("Can't suspend or hibernate " +- "while 3D resources are active.\n"); ++ if (dev_priv->refuse_hibernation) + return -EBUSY; +- } + + pci_save_state(pdev); + pci_disable_device(pdev); +@@ -1321,56 +1354,62 @@ static int vmw_pm_resume(struct device *kdev) + return vmw_pci_resume(pdev); + } + +-static int vmw_pm_prepare(struct device *kdev) ++static int vmw_pm_freeze(struct device *kdev) + { + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + +- /** +- * Release 3d reference held by fbdev and potentially +- * stop fifo. +- */ + dev_priv->suspended = true; + if (dev_priv->enable_fb) +- vmw_3d_resource_dec(dev_priv, true); +- +- if (dev_priv->num_3d_resources != 0) { +- +- DRM_INFO("Can't suspend or hibernate " +- "while 3D resources are active.\n"); ++ vmw_fifo_resource_dec(dev_priv); + ++ if (atomic_read(&dev_priv->num_fifo_resources) != 0) { ++ DRM_ERROR("Can't hibernate while 3D resources are active.\n"); + if (dev_priv->enable_fb) +- vmw_3d_resource_inc(dev_priv, true); ++ vmw_fifo_resource_inc(dev_priv); ++ WARN_ON(vmw_request_device_late(dev_priv)); + dev_priv->suspended = false; + return -EBUSY; + } + ++ if (dev_priv->enable_fb) ++ __vmw_svga_disable(dev_priv); ++ ++ vmw_release_device_late(dev_priv); ++ + return 0; + } + +-static void vmw_pm_complete(struct device *kdev) ++static int vmw_pm_restore(struct device *kdev) + { + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); ++ int ret; + + vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2); + (void) vmw_read(dev_priv, SVGA_REG_ID); + +- /** +- * Reclaim 3d reference held by fbdev and potentially +- * start fifo. +- */ + if (dev_priv->enable_fb) +- vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); ++ ++ ret = vmw_request_device(dev_priv); ++ if (ret) ++ return ret; ++ ++ if (dev_priv->enable_fb) ++ __vmw_svga_enable(dev_priv); + + dev_priv->suspended = false; ++ ++ return 0; + } + + static const struct dev_pm_ops vmw_pm_ops = { +- .prepare = vmw_pm_prepare, +- .complete = vmw_pm_complete, ++ .freeze = vmw_pm_freeze, ++ .thaw = vmw_pm_restore, ++ .restore = vmw_pm_restore, + .suspend = vmw_pm_suspend, + .resume = vmw_pm_resume, + }; +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +index d26a6daa9719..a5f221eaf076 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +@@ -484,6 +484,7 @@ struct vmw_private { + + bool stealth; + bool enable_fb; ++ spinlock_t svga_lock; + + /** + * Master management. +@@ -493,9 +494,10 @@ struct vmw_private { + struct vmw_master fbdev_master; + struct notifier_block pm_nb; + bool suspended; ++ bool refuse_hibernation; + + struct mutex release_mutex; +- uint32_t num_3d_resources; ++ atomic_t num_fifo_resources; + + /* + * Replace this with an rwsem as soon as we have down_xx_interruptible() +@@ -587,8 +589,9 @@ static inline uint32_t vmw_read(struct vmw_private *dev_priv, + return val; + } + +-int vmw_3d_resource_inc(struct vmw_private *dev_priv, bool unhide_svga); +-void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga); ++extern void vmw_svga_enable(struct vmw_private *dev_priv); ++extern void vmw_svga_disable(struct vmw_private *dev_priv); ++ + + /** + * GMR utilities - vmwgfx_gmr.c +@@ -1116,4 +1119,14 @@ static inline struct ttm_mem_global *vmw_mem_glob(struct vmw_private *dev_priv) + { + return (struct ttm_mem_global *) dev_priv->mem_global_ref.object; + } ++ ++static inline void vmw_fifo_resource_inc(struct vmw_private *dev_priv) ++{ ++ atomic_inc(&dev_priv->num_fifo_resources); ++} ++ ++static inline void vmw_fifo_resource_dec(struct vmw_private *dev_priv) ++{ ++ atomic_dec(&dev_priv->num_fifo_resources); ++} + #endif +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +index 0a474f391fad..0e062613a7db 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +@@ -596,7 +596,10 @@ int vmw_fb_off(struct vmw_private *vmw_priv) + + info = vmw_priv->fb_info; + par = info->par; ++ if (!par->bo_ptr) ++ return 0; + ++ vmw_kms_save_vga(vmw_priv); + spin_lock_irqsave(&par->dirty.lock, flags); + par->dirty.active = false; + spin_unlock_irqrestore(&par->dirty.lock, flags); +@@ -648,6 +651,7 @@ int vmw_fb_on(struct vmw_private *vmw_priv) + spin_lock_irqsave(&par->dirty.lock, flags); + par->dirty.active = true; + spin_unlock_irqrestore(&par->dirty.lock, flags); ++ vmw_kms_restore_vga(vmw_priv); + + err_no_buffer: + vmw_fb_set_par(info); +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +index 39f2b03888e7..cd5d9f3fe0e0 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +@@ -98,7 +98,6 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) + __le32 __iomem *fifo_mem = dev_priv->mmio_virt; + uint32_t max; + uint32_t min; +- uint32_t dummy; + + fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE; + fifo->static_buffer = vmalloc(fifo->static_buffer_size); +@@ -112,10 +111,6 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) + mutex_init(&fifo->fifo_mutex); + init_rwsem(&fifo->rwsem); + +- /* +- * Allow mapping the first page read-only to user-space. +- */ +- + DRM_INFO("width %d\n", vmw_read(dev_priv, SVGA_REG_WIDTH)); + DRM_INFO("height %d\n", vmw_read(dev_priv, SVGA_REG_HEIGHT)); + DRM_INFO("bpp %d\n", vmw_read(dev_priv, SVGA_REG_BITS_PER_PIXEL)); +@@ -123,7 +118,9 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) + dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE); + dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE); + dev_priv->traces_state = vmw_read(dev_priv, SVGA_REG_TRACES); +- vmw_write(dev_priv, SVGA_REG_ENABLE, 1); ++ ++ vmw_write(dev_priv, SVGA_REG_ENABLE, SVGA_REG_ENABLE_ENABLE_HIDE); ++ vmw_write(dev_priv, SVGA_REG_TRACES, 0); + + min = 4; + if (dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO) +@@ -155,7 +152,8 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) + atomic_set(&dev_priv->marker_seq, dev_priv->last_read_seqno); + iowrite32(dev_priv->last_read_seqno, fifo_mem + SVGA_FIFO_FENCE); + vmw_marker_queue_init(&fifo->marker_queue); +- return vmw_fifo_send_fence(dev_priv, &dummy); ++ ++ return 0; + } + + void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason) +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +index 5c289f748ab4..53579f278b63 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +@@ -280,6 +280,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) + } + + vmw_fb_off(dev_priv); ++ vmw_svga_enable(dev_priv); + + crtc->primary->fb = fb; + encoder->crtc = crtc; +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c +index 04a64b8cd3cd..f06d60f41fa7 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c +@@ -574,7 +574,7 @@ void vmw_mob_unbind(struct vmw_private *dev_priv, + vmw_fence_single_bo(bo, NULL); + ttm_bo_unreserve(bo); + } +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + } + + /* +@@ -627,7 +627,7 @@ int vmw_mob_bind(struct vmw_private *dev_priv, + mob->pt_level += VMW_MOBFMT_PTDEPTH_1 - SVGA3D_MOBFMT_PTDEPTH_1; + } + +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { +@@ -648,7 +648,7 @@ int vmw_mob_bind(struct vmw_private *dev_priv, + return 0; + + out_no_cmd_space: +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + if (pt_set_up) + ttm_bo_unref(&mob->pt_bo); + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +index 7dc591d04d9a..9e8eb364a6ac 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +@@ -332,6 +332,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set) + } + + vmw_fb_off(dev_priv); ++ vmw_svga_enable(dev_priv); + + if (mode->hdisplay != crtc->mode.hdisplay || + mode->vdisplay != crtc->mode.vdisplay) { +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +index 6a4584a43aa6..6110a433ebfe 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +@@ -165,7 +165,7 @@ static int vmw_gb_shader_create(struct vmw_resource *res) + cmd->body.type = shader->type; + cmd->body.sizeInBytes = shader->size; + vmw_fifo_commit(dev_priv, sizeof(*cmd)); +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + + return 0; + +@@ -275,7 +275,7 @@ static int vmw_gb_shader_destroy(struct vmw_resource *res) + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + mutex_unlock(&dev_priv->binding_mutex); + vmw_resource_release_id(res); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + + return 0; + } +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +index 4ecdbf3e59da..4d0c98edeb6a 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +@@ -340,7 +340,7 @@ static void vmw_hw_surface_destroy(struct vmw_resource *res) + dev_priv->used_memory_size -= res->backup_size; + mutex_unlock(&dev_priv->cmdbuf_mutex); + } +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + } + + /** +@@ -576,14 +576,14 @@ static int vmw_surface_init(struct vmw_private *dev_priv, + + BUG_ON(res_free == NULL); + if (!dev_priv->has_mob) +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + ret = vmw_resource_init(dev_priv, res, true, res_free, + (dev_priv->has_mob) ? &vmw_gb_surface_func : + &vmw_legacy_surface_func); + + if (unlikely(ret != 0)) { + if (!dev_priv->has_mob) +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + res_free(res); + return ret; + } +@@ -1028,7 +1028,7 @@ static int vmw_gb_surface_create(struct vmw_resource *res) + if (likely(res->id != -1)) + return 0; + +- (void) vmw_3d_resource_inc(dev_priv, false); ++ vmw_fifo_resource_inc(dev_priv); + ret = vmw_resource_alloc_id(res); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate a surface id.\n"); +@@ -1068,7 +1068,7 @@ static int vmw_gb_surface_create(struct vmw_resource *res) + out_no_fifo: + vmw_resource_release_id(res); + out_no_id: +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + return ret; + } + +@@ -1213,7 +1213,7 @@ static int vmw_gb_surface_destroy(struct vmw_resource *res) + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + mutex_unlock(&dev_priv->binding_mutex); + vmw_resource_release_id(res); +- vmw_3d_resource_dec(dev_priv, false); ++ vmw_fifo_resource_dec(dev_priv); + + return 0; + } +-- +2.4.3 + diff --git a/x86-apic-Fix-fallout-from-x2apic-cleanup.patch b/x86-apic-Fix-fallout-from-x2apic-cleanup.patch new file mode 100644 index 000000000..8f7b578dd --- /dev/null +++ b/x86-apic-Fix-fallout-from-x2apic-cleanup.patch @@ -0,0 +1,81 @@ +From a57e456a7b28431b55e407e5ab78ebd5b378d19e Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Sat, 22 Aug 2015 16:41:17 +0200 +Subject: [PATCH] x86/apic: Fix fallout from x2apic cleanup + +In the recent x2apic cleanup I got two things really wrong: +1) The safety check in __disable_x2apic which allows the function to + be called unconditionally is backwards. The check is there to + prevent access to the apic MSR in case that the machine has no + apic. Though right now it returns if the machine has an apic and + therefor the disabling of x2apic is never invoked. + +2) x2apic_disable() sets x2apic_mode to 0 after registering the local + apic. That's wrong, because register_lapic_address() checks x2apic + mode and therefor takes the wrong code path. + +This results in boot failures on machines with x2apic preenabled by +BIOS and can also lead to an fatal MSR access on machines without +apic. + +The solutions are simple: +1) Correct the sanity check for apic availability +2) Clear x2apic_mode _before_ calling register_lapic_address() + +Fixes: 659006bf3ae3 'x86/x2apic: Split enable and setup function' +Reported-and-tested-by: Javier Monteagudo +Signed-off-by: Thomas Gleixner +Link: https://bugzilla.redhat.com/show_bug.cgi?id=1224764 +Cc: stable@vger.kernel.org # 4.0+ +Cc: Laura Abbott +Cc: Jiang Liu +Cc: Joerg Roedel +Cc: Tony Luck +Cc: Borislav Petkov +--- + arch/x86/kernel/apic/apic.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c +index dcb5285..cde732c 100644 +--- a/arch/x86/kernel/apic/apic.c ++++ b/arch/x86/kernel/apic/apic.c +@@ -1424,7 +1424,7 @@ static inline void __x2apic_disable(void) + { + u64 msr; + +- if (cpu_has_apic) ++ if (!cpu_has_apic) + return; + + rdmsrl(MSR_IA32_APICBASE, msr); +@@ -1483,10 +1483,13 @@ void x2apic_setup(void) + + static __init void x2apic_disable(void) + { +- u32 x2apic_id; ++ u32 x2apic_id, state = x2apic_state; + +- if (x2apic_state != X2APIC_ON) +- goto out; ++ x2apic_mode = 0; ++ x2apic_state = X2APIC_DISABLED; ++ ++ if (state != X2APIC_ON) ++ return; + + x2apic_id = read_apic_id(); + if (x2apic_id >= 255) +@@ -1494,9 +1497,6 @@ static __init void x2apic_disable(void) + + __x2apic_disable(); + register_lapic_address(mp_lapic_addr); +-out: +- x2apic_state = X2APIC_DISABLED; +- x2apic_mode = 0; + } + + static __init void x2apic_enable(void) +-- +2.4.3 + diff --git a/x86-build-Build-compressed-x86-kernels-as-PIE.patch b/x86-build-Build-compressed-x86-kernels-as-PIE.patch new file mode 100644 index 000000000..064cb485b --- /dev/null +++ b/x86-build-Build-compressed-x86-kernels-as-PIE.patch @@ -0,0 +1,159 @@ +From 6d92bc9d483aa1751755a66fee8fb39dffb088c0 Mon Sep 17 00:00:00 2001 +From: "H.J. Lu" +Date: Wed, 16 Mar 2016 20:04:35 -0700 +Subject: [PATCH] x86/build: Build compressed x86 kernels as PIE + +The 32-bit x86 assembler in binutils 2.26 will generate R_386_GOT32X +relocation to get the symbol address in PIC. When the compressed x86 +kernel isn't built as PIC, the linker optimizes R_386_GOT32X relocations +to their fixed symbol addresses. However, when the compressed x86 +kernel is loaded at a different address, it leads to the following +load failure: + + Failed to allocate space for phdrs + +during the decompression stage. + +If the compressed x86 kernel is relocatable at run-time, it should be +compiled with -fPIE, instead of -fPIC, if possible and should be built as +Position Independent Executable (PIE) so that linker won't optimize +R_386_GOT32X relocation to its fixed symbol address. + +Older linkers generate R_386_32 relocations against locally defined +symbols, _bss, _ebss, _got and _egot, in PIE. It isn't wrong, just less +optimal than R_386_RELATIVE. But the x86 kernel fails to properly handle +R_386_32 relocations when relocating the kernel. To generate +R_386_RELATIVE relocations, we mark _bss, _ebss, _got and _egot as +hidden in both 32-bit and 64-bit x86 kernels. + +To build a 64-bit compressed x86 kernel as PIE, we need to disable the +relocation overflow check to avoid relocation overflow errors. We do +this with a new linker command-line option, -z noreloc-overflow, which +got added recently: + + commit 4c10bbaa0912742322f10d9d5bb630ba4e15dfa7 + Author: H.J. Lu + Date: Tue Mar 15 11:07:06 2016 -0700 + + Add -z noreloc-overflow option to x86-64 ld + + Add -z noreloc-overflow command-line option to the x86-64 ELF linker to + disable relocation overflow check. This can be used to avoid relocation + overflow check if there will be no dynamic relocation overflow at + run-time. + +The 64-bit compressed x86 kernel is built as PIE only if the linker supports +-z noreloc-overflow. So far 64-bit relocatable compressed x86 kernel +boots fine even when it is built as a normal executable. + +Signed-off-by: H.J. Lu +Cc: Andy Lutomirski +Cc: Borislav Petkov +Cc: Brian Gerst +Cc: Denys Vlasenko +Cc: H. Peter Anvin +Cc: Linus Torvalds +Cc: Peter Zijlstra +Cc: Thomas Gleixner +Cc: linux-kernel@vger.kernel.org +[ Edited the changelog and comments. ] +Signed-off-by: Ingo Molnar +--- + arch/x86/boot/compressed/Makefile | 14 +++++++++++++- + arch/x86/boot/compressed/head_32.S | 28 ++++++++++++++++++++++++++++ + arch/x86/boot/compressed/head_64.S | 8 ++++++++ + 3 files changed, 49 insertions(+), 1 deletion(-) + +diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile +index 6915ff2..8774cb2 100644 +--- a/arch/x86/boot/compressed/Makefile ++++ b/arch/x86/boot/compressed/Makefile +@@ -26,7 +26,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \ + vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4 + + KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2 +-KBUILD_CFLAGS += -fno-strict-aliasing -fPIC ++KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC) + KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING + cflags-$(CONFIG_X86_32) := -march=i386 + cflags-$(CONFIG_X86_64) := -mcmodel=small +@@ -40,6 +40,18 @@ GCOV_PROFILE := n + UBSAN_SANITIZE :=n + + LDFLAGS := -m elf_$(UTS_MACHINE) ++ifeq ($(CONFIG_RELOCATABLE),y) ++# If kernel is relocatable, build compressed kernel as PIE. ++ifeq ($(CONFIG_X86_32),y) ++LDFLAGS += $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker) ++else ++# To build 64-bit compressed kernel as PIE, we disable relocation ++# overflow check to avoid relocation overflow error with a new linker ++# command-line option, -z noreloc-overflow. ++LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \ ++ && echo "-z noreloc-overflow -pie --no-dynamic-linker") ++endif ++endif + LDFLAGS_vmlinux := -T + + hostprogs-y := mkpiggy +diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S +index 8ef964d..0256064 100644 +--- a/arch/x86/boot/compressed/head_32.S ++++ b/arch/x86/boot/compressed/head_32.S +@@ -31,6 +31,34 @@ + #include + #include + ++/* ++ * The 32-bit x86 assembler in binutils 2.26 will generate R_386_GOT32X ++ * relocation to get the symbol address in PIC. When the compressed x86 ++ * kernel isn't built as PIC, the linker optimizes R_386_GOT32X ++ * relocations to their fixed symbol addresses. However, when the ++ * compressed x86 kernel is loaded at a different address, it leads ++ * to the following load failure: ++ * ++ * Failed to allocate space for phdrs ++ * ++ * during the decompression stage. ++ * ++ * If the compressed x86 kernel is relocatable at run-time, it should be ++ * compiled with -fPIE, instead of -fPIC, if possible and should be built as ++ * Position Independent Executable (PIE) so that linker won't optimize ++ * R_386_GOT32X relocation to its fixed symbol address. Older ++ * linkers generate R_386_32 relocations against locally defined symbols, ++ * _bss, _ebss, _got and _egot, in PIE. It isn't wrong, just less ++ * optimal than R_386_RELATIVE. But the x86 kernel fails to properly handle ++ * R_386_32 relocations when relocating the kernel. To generate ++ * R_386_RELATIVE relocations, we mark _bss, _ebss, _got and _egot as ++ * hidden: ++ */ ++ .hidden _bss ++ .hidden _ebss ++ .hidden _got ++ .hidden _egot ++ + __HEAD + ENTRY(startup_32) + #ifdef CONFIG_EFI_STUB +diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S +index b0c0d16..86558a1 100644 +--- a/arch/x86/boot/compressed/head_64.S ++++ b/arch/x86/boot/compressed/head_64.S +@@ -33,6 +33,14 @@ + #include + #include + ++/* ++ * Locally defined symbols should be marked hidden: ++ */ ++ .hidden _bss ++ .hidden _ebss ++ .hidden _got ++ .hidden _egot ++ + __HEAD + .code32 + ENTRY(startup_32) +-- +2.7.3 + diff --git a/x86-efi-bgrt-Switch-all-pr_err-to-pr_debug-for-inval.patch b/x86-efi-bgrt-Switch-all-pr_err-to-pr_debug-for-inval.patch new file mode 100644 index 000000000..d3d0aa2c3 --- /dev/null +++ b/x86-efi-bgrt-Switch-all-pr_err-to-pr_debug-for-inval.patch @@ -0,0 +1,94 @@ +From 3e4f68f273ef86e6ed8be24a86f8ef514deaecc0 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Wed, 27 Apr 2016 08:37:41 -0400 +Subject: [PATCH] x86/efi-bgrt: Switch all pr_err() to pr_debug() for invalid + BGRT + +The promise of pretty boot splashes from firmware via BGRT was at +best only that; a promise. The kernel diligently checks to make +sure the BGRT data firmware gives it is valid, and dutifully warns +the user when it isn't. However, it does so via the pr_err log +level which seems unnecessary. The user cannot do anything about +this and there really isn't an error on the part of Linux to +correct. + +This lowers the log level by using pr_debug instead. Users will +no longer have their boot process uglified by the kernel reminding +us that firmware can and often is broken. Ironic, considering +BGRT is supposed to make boot pretty to begin with. + +Signed-off-by: Josh Boyer +--- + arch/x86/platform/efi/efi-bgrt.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/arch/x86/platform/efi/efi-bgrt.c b/arch/x86/platform/efi/efi-bgrt.c +index ea48449b2e63..87da4108785b 100644 +--- a/arch/x86/platform/efi/efi-bgrt.c ++++ b/arch/x86/platform/efi/efi-bgrt.c +@@ -41,17 +41,17 @@ void __init efi_bgrt_init(void) + return; + + if (bgrt_tab->header.length < sizeof(*bgrt_tab)) { +- pr_err("Ignoring BGRT: invalid length %u (expected %zu)\n", ++ pr_debug("Ignoring BGRT: invalid length %u (expected %zu)\n", + bgrt_tab->header.length, sizeof(*bgrt_tab)); + return; + } + if (bgrt_tab->version != 1) { +- pr_err("Ignoring BGRT: invalid version %u (expected 1)\n", ++ pr_debug("Ignoring BGRT: invalid version %u (expected 1)\n", + bgrt_tab->version); + return; + } + if (bgrt_tab->status & 0xfe) { +- pr_err("Ignoring BGRT: reserved status bits are non-zero %u\n", ++ pr_debug("Ignoring BGRT: reserved status bits are non-zero %u\n", + bgrt_tab->status); + return; + } +@@ -61,12 +61,12 @@ void __init efi_bgrt_init(void) + return; + } + if (bgrt_tab->image_type != 0) { +- pr_err("Ignoring BGRT: invalid image type %u (expected 0)\n", ++ pr_debug("Ignoring BGRT: invalid image type %u (expected 0)\n", + bgrt_tab->image_type); + return; + } + if (!bgrt_tab->image_address) { +- pr_err("Ignoring BGRT: null image address\n"); ++ pr_debug("Ignoring BGRT: null image address\n"); + return; + } + +@@ -76,7 +76,7 @@ void __init efi_bgrt_init(void) + sizeof(bmp_header)); + ioremapped = true; + if (!image) { +- pr_err("Ignoring BGRT: failed to map image header memory\n"); ++ pr_debug("Ignoring BGRT: failed to map image header memory\n"); + return; + } + } +@@ -88,7 +88,7 @@ void __init efi_bgrt_init(void) + + bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL | __GFP_NOWARN); + if (!bgrt_image) { +- pr_err("Ignoring BGRT: failed to allocate memory for image (wanted %zu bytes)\n", ++ pr_debug("Ignoring BGRT: failed to allocate memory for image (wanted %zu bytes)\n", + bgrt_image_size); + return; + } +@@ -97,7 +97,7 @@ void __init efi_bgrt_init(void) + image = early_ioremap(bgrt_tab->image_address, + bmp_header.size); + if (!image) { +- pr_err("Ignoring BGRT: failed to map image memory\n"); ++ pr_debug("Ignoring BGRT: failed to map image memory\n"); + kfree(bgrt_image); + bgrt_image = NULL; + return; +-- +2.5.5 + diff --git a/x86-mm-32-Enable-full-randomization-on-i386-and-X86_.patch b/x86-mm-32-Enable-full-randomization-on-i386-and-X86_.patch new file mode 100644 index 000000000..0776982c1 --- /dev/null +++ b/x86-mm-32-Enable-full-randomization-on-i386-and-X86_.patch @@ -0,0 +1,83 @@ +From 8b8addf891de8a00e4d39fc32f93f7c5eb8feceb Mon Sep 17 00:00:00 2001 +From: Hector Marco-Gisbert +Date: Thu, 10 Mar 2016 20:51:00 +0100 +Subject: [PATCH] x86/mm/32: Enable full randomization on i386 and X86_32 + +Currently on i386 and on X86_64 when emulating X86_32 in legacy mode, only +the stack and the executable are randomized but not other mmapped files +(libraries, vDSO, etc.). This patch enables randomization for the +libraries, vDSO and mmap requests on i386 and in X86_32 in legacy mode. + +By default on i386 there are 8 bits for the randomization of the libraries, +vDSO and mmaps which only uses 1MB of VA. + +This patch preserves the original randomness, using 1MB of VA out of 3GB or +4GB. We think that 1MB out of 3GB is not a big cost for having the ASLR. + +The first obvious security benefit is that all objects are randomized (not +only the stack and the executable) in legacy mode which highly increases +the ASLR effectiveness, otherwise the attackers may use these +non-randomized areas. But also sensitive setuid/setgid applications are +more secure because currently, attackers can disable the randomization of +these applications by setting the ulimit stack to "unlimited". This is a +very old and widely known trick to disable the ASLR in i386 which has been +allowed for too long. + +Another trick used to disable the ASLR was to set the ADDR_NO_RANDOMIZE +personality flag, but fortunately this doesn't work on setuid/setgid +applications because there is security checks which clear Security-relevant +flags. + +This patch always randomizes the mmap_legacy_base address, removing the +possibility to disable the ASLR by setting the stack to "unlimited". + +Signed-off-by: Hector Marco-Gisbert +Acked-by: Ismael Ripoll Ripoll +Acked-by: Kees Cook +Acked-by: Arjan van de Ven +Cc: Linus Torvalds +Cc: Peter Zijlstra +Cc: Thomas Gleixner +Cc: akpm@linux-foundation.org +Cc: kees Cook +Link: http://lkml.kernel.org/r/1457639460-5242-1-git-send-email-hecmargi@upv.es +Signed-off-by: Ingo Molnar +--- + arch/x86/mm/mmap.c | 14 +------------- + 1 file changed, 1 insertion(+), 13 deletions(-) + +diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c +index 96bd1e2..389939f 100644 +--- a/arch/x86/mm/mmap.c ++++ b/arch/x86/mm/mmap.c +@@ -94,18 +94,6 @@ static unsigned long mmap_base(unsigned long rnd) + } + + /* +- * Bottom-up (legacy) layout on X86_32 did not support randomization, X86_64 +- * does, but not when emulating X86_32 +- */ +-static unsigned long mmap_legacy_base(unsigned long rnd) +-{ +- if (mmap_is_ia32()) +- return TASK_UNMAPPED_BASE; +- else +- return TASK_UNMAPPED_BASE + rnd; +-} +- +-/* + * This function, called very early during the creation of a new + * process VM image, sets up which VM layout function to use: + */ +@@ -116,7 +104,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm) + if (current->flags & PF_RANDOMIZE) + random_factor = arch_mmap_rnd(); + +- mm->mmap_legacy_base = mmap_legacy_base(random_factor); ++ mm->mmap_legacy_base = TASK_UNMAPPED_BASE + random_factor; + + if (mmap_is_legacy()) { + mm->mmap_base = mm->mmap_legacy_base; +-- +2.7.3 +